import sys
import os
import pandas as pd
import re
import numpy as np
import traceback
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QLineEdit, QPushButton, QFileDialog, QMessageBox,
                             QSpinBox, QFormLayout, QTextEdit, QGroupBox, QTabWidget, 
                             QTableWidget, QTableWidgetItem, QHeaderView, QDoubleSpinBox,
                             QStackedWidget, QFrame, QComboBox, QGridLayout)
from PyQt5.QtCore import Qt, QSettings
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib as mpl
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar

class IncrementalDataProcessor(QMainWindow):
    
    class ChartWindow(QMainWindow):
        """独立的图表显示窗口"""
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("图表分析")
            self.setGeometry(100, 100, 1200, 800)
            
            # 中央部件
            central_widget = QWidget()
            self.setCentralWidget(central_widget)
            layout = QVBoxLayout(central_widget)
            
            # 图表区域
            self.figure = plt.figure(figsize=(12, 9), dpi=100)
            self.canvas = FigureCanvas(self.figure)
            layout.addWidget(self.canvas)
            
            # 工具栏
            self.toolbar = NavigationToolbar(self.canvas, self)
            layout.addWidget(self.toolbar)
            
            # 导出按钮
            export_btn = QPushButton("导出图表")
            export_btn.clicked.connect(self.export_chart)
            layout.addWidget(export_btn)
        
        def export_chart(self):
            """导出当前图表为图片"""
            if not hasattr(self, 'figure') or not self.figure:
                QMessageBox.warning(self, "警告", "没有可导出的图表")
                return
                
            # 获取保存路径
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存图表", "", 
                "PNG图片 (*.png);;JPEG图片 (*.jpg);;PDF文件 (*.pdf);;SVG文件 (*.svg)"
            )
            
            if not file_path:
                return
                
            try:
                # 根据扩展名保存
                if file_path.endswith('.png'):
                    self.figure.savefig(file_path, dpi=300, format='png')
                elif file_path.endswith('.jpg') or file_path.endswith('.jpeg'):
                    self.figure.savefig(file_path, dpi=300, format='jpeg')
                elif file_path.endswith('.pdf'):
                    self.figure.savefig(file_path, format='pdf')
                elif file_path.endswith('.svg'):
                    self.figure.savefig(file_path, format='svg')
                else:
                    # 默认保存为PNG
                    file_path += '.png'
                    self.figure.savefig(file_path, dpi=300, format='png')
                    
                QMessageBox.information(self, "导出成功", f"图表已保存到:\n{file_path}")
            except Exception as e:
                QMessageBox.critical(self, "导出失败", f"保存图表时出错:\n{str(e)}")

    def __init__(self):
        super().__init__()
        
        # 设置支持中文的字体
        mpl.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Zen Hei']
        mpl.rcParams['axes.unicode_minus'] = False
        
        # 合约名称映射
        self.contract_name_map = {
            'IC': '中证500',
            'IF': '沪深300',
            'IH': '上证50',
            'IM': '中证1000',
            'SH': '上证指数'
        }
        
        self.setWindowTitle("净空单与指数关系分析工具")
        self.setGeometry(100, 100, 1200, 900)
        
        # 中央部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 创建文件输入字段
        file_group = QGroupBox("文件输入")
        file_layout = QVBoxLayout(file_group)
        
        self.file_fields = []
        file_labels = [
            "净空单历史数据文件:",
            "中证500指数数据文件:",
            "沪深300指数数据文件:",
            "上证50指数数据文件:",
            "中证1000指数数据文件:",
            "上证指数数据文件:"
        ]
        
        for label in file_labels:
            layout = QHBoxLayout()
            layout.addWidget(QLabel(label))
            
            line_edit = QLineEdit()
            line_edit.setReadOnly(True)
            layout.addWidget(line_edit)
            
            browse_btn = QPushButton("浏览...")
            browse_btn.clicked.connect(self.create_browse_handler(line_edit))
            layout.addWidget(browse_btn)
            
            self.file_fields.append(line_edit)
            file_layout.addLayout(layout)
        
        main_layout.addWidget(file_group)
        
        # 参数设置区域
        params_group = QGroupBox("参数设置")
        params_layout = QFormLayout(params_group)
        
        # T+N天数参数
        self.lag_days_spin = QSpinBox()
        self.lag_days_spin.setRange(0, 10)  # 0-10天滞后
        self.lag_days_spin.setValue(0)  # 默认无滞后
        self.lag_days_spin.setToolTip("设置指数变化量相对于净空单变化量的滞后天数")
        params_layout.addRow("指数T+N天数:", self.lag_days_spin)
        
        # 最大T+N天数参数 - 默认值设为3
        self.max_lag_spin = QSpinBox()
        self.max_lag_spin.setRange(1, 20)  # 1-20天最大滞后
        self.max_lag_spin.setValue(3)  # 默认3天
        self.max_lag_spin.setToolTip("设置最大滞后天数用于比较统计")
        params_layout.addRow("最大T+N天数:", self.max_lag_spin)
        
        # 显著变化阈值
        self.std_threshold_spin = QDoubleSpinBox()
        self.std_threshold_spin.setRange(0.5, 3.0)  # 0.5-3倍标准差
        self.std_threshold_spin.setValue(1.0)  # 默认1倍标准差
        self.std_threshold_spin.setSingleStep(0.1)
        self.std_threshold_spin.setToolTip("设置净空单显著变化的阈值（标准差倍数）")
        params_layout.addRow("显著变化阈值:", self.std_threshold_spin)
        
        # 分位数阈值
        self.quantile_spin = QDoubleSpinBox()
        self.quantile_spin.setRange(75.0, 99.0)  # 75%-99%
        self.quantile_spin.setValue(90.0)  # 默认90%
        self.quantile_spin.setSingleStep(0.5)
        self.quantile_spin.setToolTip("设置净空单显著变化的分位数阈值（%）")
        params_layout.addRow("分位数阈值(%):", self.quantile_spin)
        
        # 显著性阈值方法选择 - 修改选项：只保留三种方法
        self.threshold_method_combo = QComboBox()
        self.threshold_method_combo.addItems(["固定阈值", "滚动阈值", "分位数阈值"])
        self.threshold_method_combo.setCurrentIndex(0)  # 默认选择"固定阈值"
        self.threshold_method_combo.setToolTip("选择显著性分析使用的阈值方法")
        params_layout.addRow("显著性阈值方法:", self.threshold_method_combo)
        
        # 输出目录选择
        self.output_dir_edit = QLineEdit()
        self.output_dir_edit.setReadOnly(True)
        params_layout.addRow("输出目录:", self.output_dir_edit)
        
        output_browse_btn = QPushButton("浏览...")
        output_browse_btn.clicked.connect(self.browse_output_dir)
        params_layout.addRow("", output_browse_btn)
        
        main_layout.addWidget(params_group)
        
        # 按钮区域
        button_layout = QHBoxLayout()
        
        # 处理按钮
        process_btn = QPushButton("处理数据")
        process_btn.clicked.connect(self.process_data)
        button_layout.addWidget(process_btn)
        
        # 比较统计按钮
        compare_btn = QPushButton("执行比较统计")
        compare_btn.clicked.connect(self.run_comparison)
        button_layout.addWidget(compare_btn)
        
        # 添加保存配置按钮
        save_config_btn = QPushButton("保存配置")
        save_config_btn.clicked.connect(self.save_config)
        button_layout.addWidget(save_config_btn)
        
        main_layout.addLayout(button_layout)
        
        # 结果标签页
        self.tab_widget = QTabWidget()
        main_layout.addWidget(self.tab_widget)
        
        # 统计结果标签页
        self.stats_tab = QWidget()
        self.stats_layout = QVBoxLayout(self.stats_tab)
        
        self.stats_text = QTextEdit()
        self.stats_text.setReadOnly(True)
        self.stats_layout.addWidget(self.stats_text)
        
        self.stats_tab_export_btn = QPushButton("导出统计结果为CSV")
        self.stats_layout.addWidget(self.stats_tab_export_btn)
        
        self.tab_widget.addTab(self.stats_tab, "统计分析")
        
        # 比较统计标签页
        self.compare_tab = QWidget()
        self.compare_layout = QVBoxLayout(self.compare_tab)
        
        # 创建切换按钮区域
        button_frame = QFrame()
        button_frame.setFrameShape(QFrame.StyledPanel)
        button_layout = QHBoxLayout(button_frame)
        
        # 创建表格切换按钮
        self.contract_compare_btn = QPushButton("按指数比较")
        self.contract_compare_btn.setCheckable(True)
        self.contract_compare_btn.setChecked(True)
        self.contract_compare_btn.clicked.connect(lambda: self.show_table(0))
        button_layout.addWidget(self.contract_compare_btn)
        
        #button_layout.addWidget(self.lag_compare_btn)
        
        self.delayed_btn = QPushButton("补涨补跌分析")
        self.delayed_btn.setCheckable(True)
        self.delayed_btn.clicked.connect(lambda: self.show_table(2))
        button_layout.addWidget(self.delayed_btn)
        
        self.significant_btn = QPushButton("显著变化分析")
        self.significant_btn.setCheckable(True)
        self.significant_btn.clicked.connect(lambda: self.show_table(3))
        button_layout.addWidget(self.significant_btn)
        
        # 添加按钮组
        self.button_group = [self.contract_compare_btn, self.delayed_btn, self.significant_btn]
        
        self.compare_layout.addWidget(button_frame)
        
        # 创建堆叠窗口
        self.stacked_widget = QStackedWidget()
        self.compare_layout.addWidget(self.stacked_widget)
        
        # 按指数比较表格
        self.contract_comparison_table = QTableWidget()
        self.contract_comparison_table.setColumnCount(0)
        self.contract_comparison_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        contract_widget = QWidget()
        contract_layout = QVBoxLayout(contract_widget)
        contract_layout.addWidget(QLabel("按指数比较（同一T+N天数下不同指数的看跌和看涨概率）"))
        contract_layout.addWidget(self.contract_comparison_table)
        self.stacked_widget.addWidget(contract_widget)
        
        
        # 补涨补跌表格
        self.delayed_table = QTableWidget()
        self.delayed_table.setColumnCount(0)
        self.delayed_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        delayed_widget = QWidget()
        delayed_layout = QVBoxLayout(delayed_widget)
        delayed_layout.addWidget(QLabel("补涨补跌概率（净空单增加后第k天下跌的概率）"))
        delayed_layout.addWidget(self.delayed_table)
        self.stacked_widget.addWidget(delayed_widget)
        
        # 显著变化分析表格
        self.significant_table = QTableWidget()
        self.significant_table.setColumnCount(0)
        self.significant_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        significant_widget = QWidget()
        significant_layout = QVBoxLayout(significant_widget)
        significant_layout.addWidget(QLabel("净空单显著变化影响分析"))
        significant_layout.addWidget(self.significant_table)
        self.stacked_widget.addWidget(significant_widget)
        
        self.compare_export_btn = QPushButton("导出比较结果为CSV")
        self.compare_layout.addWidget(self.compare_export_btn)
        
        # 图表标签页 - 改为按钮形式
        self.chart_tab = QWidget()
        self.chart_layout = QVBoxLayout(self.chart_tab)
        
        # 添加说明标签
        self.chart_layout.addWidget(QLabel("点击下方按钮在新窗口中查看图表分析"))
        
        # 添加按钮组
        button_group = QGroupBox("图表分析")
        button_layout = QVBoxLayout(button_group)
        
        # 比较统计图表按钮
        self.comparison_chart_btn = QPushButton("查看比较统计图表")
        self.comparison_chart_btn.clicked.connect(self.show_comparison_chart)
        button_layout.addWidget(self.comparison_chart_btn)
        
        # 合并后的显著性曲线按钮
        self.significance_curve_btn = QPushButton("绘制并查看显著性变化曲线")
        self.significance_curve_btn.clicked.connect(self.plot_and_show_significance_curve)
        button_layout.addWidget(self.significance_curve_btn)
        
        self.chart_layout.addWidget(button_group)
        
        # 在图表标签页中添加设置选项卡
        settings_tabs = QTabWidget()
        self.chart_layout.addWidget(settings_tabs)
        
        # 创建曲线参数选项卡
        curve_tab = QWidget()
        curve_layout = QVBoxLayout(curve_tab)
        settings_tabs.addTab(curve_tab, "显著性曲线参数")
        
        # 显著性分析曲线绘制区域 - 简化布局
        sig_curve_group = QGroupBox("基本参数")
        sig_curve_layout = QGridLayout(sig_curve_group)
        curve_layout.addWidget(sig_curve_group)
        
        # 第一行：指数选择和方法选择
        sig_curve_layout.addWidget(QLabel("选择指数:"), 0, 0)
        self.curve_contract_combo = QComboBox()
        for contract in ['IC', 'IF', 'IH', 'IM', 'SH']:
            chinese_name = self.contract_name_map.get(contract, contract)
            self.curve_contract_combo.addItem(f"{contract} ({chinese_name})", contract)
        sig_curve_layout.addWidget(self.curve_contract_combo, 0, 1)
        
        sig_curve_layout.addWidget(QLabel("阈值方法:"), 0, 2)
        self.curve_method_combo = QComboBox()
        self.curve_method_combo.addItem("固定阈值", "fixed")
        self.curve_method_combo.addItem("滚动阈值", "rolling")
        self.curve_method_combo.addItem("分位数阈值", "quantile")
        sig_curve_layout.addWidget(self.curve_method_combo, 0, 3, 1, 2)  # 跨两列
        
        # 添加图表类型选择
        sig_curve_layout.addWidget(QLabel("图表类型:"), 3, 0)
        
        # 第二行：阈值范围设置
        sig_curve_layout.addWidget(QLabel("阈值范围:"), 1, 0)
        sig_curve_layout.addWidget(QLabel("从"), 1, 1)
        self.start_spin = QDoubleSpinBox()
        self.start_spin.setRange(0.5, 5.0)
        self.start_spin.setValue(1.0)
        self.start_spin.setSingleStep(0.1)
        sig_curve_layout.addWidget(self.start_spin, 1, 2)
        
        sig_curve_layout.addWidget(QLabel("到"), 1, 3)
        self.end_spin = QDoubleSpinBox()
        self.end_spin.setRange(0.5, 5.0)
        self.end_spin.setValue(3.0)
        self.end_spin.setSingleStep(0.1)
        sig_curve_layout.addWidget(self.end_spin, 1, 4)
        # 图表类型选择
        self.chart_type_combo = QComboBox()
        self.chart_type_combo.addItems(["概率图", "平均变化量百分比图", "两者都显示"])
        sig_curve_layout.addWidget(self.chart_type_combo, 3, 1, 1, 2)
        
        sig_curve_layout.addWidget(QLabel("步长"), 1, 5)
        self.step_spin = QDoubleSpinBox()
        self.step_spin.setRange(0.05, 1.0)
        self.step_spin.setValue(0.1)
        self.step_spin.setSingleStep(0.05)
        sig_curve_layout.addWidget(self.step_spin, 1, 6)
        
        # 第三行：分位数范围（当选择分位数阈值时启用）
        self.quantile_label = QLabel("分位数范围:")
        sig_curve_layout.addWidget(self.quantile_label, 2, 0)
        self.quantile_start_spin = QDoubleSpinBox()
        self.quantile_start_spin.setRange(75.0, 99.9)
        self.quantile_start_spin.setValue(80.0)
        self.quantile_start_spin.setSingleStep(0.5)
        sig_curve_layout.addWidget(self.quantile_start_spin, 2, 1, 1, 2)
        
        sig_curve_layout.addWidget(QLabel("到"), 2, 3)
        self.quantile_end_spin = QDoubleSpinBox()
        self.quantile_end_spin.setRange(75.0, 99.9)
        self.quantile_end_spin.setValue(95.0)
        self.quantile_end_spin.setSingleStep(0.5)
        sig_curve_layout.addWidget(self.quantile_end_spin, 2, 4, 1, 2)
        
        # 添加连接
        self.curve_method_combo.currentIndexChanged.connect(self.toggle_quantile_range)
        self.toggle_quantile_range()  # 初始化状态
        
        # 添加导出按钮
        export_curve_btn = QPushButton("导出曲线数据")
        export_curve_btn.clicked.connect(self.export_curve_data)
        curve_layout.addWidget(export_curve_btn)
        
        # 添加绘图按钮 - 文本保持不变
        plot_curve_btn = QPushButton("绘制并查看显著性变化曲线")
        plot_curve_btn.clicked.connect(self.plot_and_show_significance_curve)
        curve_layout.addWidget(plot_curve_btn)
        
        # 原始数据选项卡
        raw_data_tab = QWidget()
        raw_layout = QVBoxLayout(raw_data_tab)
        settings_tabs.addTab(raw_data_tab, "原始数据可视化")
        
        # 数据选择区域
        data_selection_group = QGroupBox("数据选择")
        data_selection_layout = QGridLayout(data_selection_group)
        raw_layout.addWidget(data_selection_group)
        
        # 第一行：合约选择和显示方式
        data_selection_layout.addWidget(QLabel("选择合约:"), 0, 0)
        self.contract_combo = QComboBox()
        for contract in ['IC', 'IF', 'IH', 'IM', 'SH']:
            chinese_name = self.contract_name_map.get(contract, contract)
            self.contract_combo.addItem(f"{contract} ({chinese_name})", contract)
        data_selection_layout.addWidget(self.contract_combo, 0, 1)
        
        data_selection_layout.addWidget(QLabel("显示方式:"), 0, 2)
        self.display_type_combo = QComboBox()
        self.display_type_combo.addItems(["实际数据", "变化量"])
        data_selection_layout.addWidget(self.display_type_combo, 0, 3)
        
        # 第二行：时间范围选择
        data_selection_layout.addWidget(QLabel("开始日期:"), 1, 0)
        self.start_date_edit = QLineEdit()
        self.start_date_edit.setPlaceholderText("YYYYMMDD")
        self.start_date_edit.setMaximumWidth(100)
        data_selection_layout.addWidget(self.start_date_edit, 1, 1)
        
        data_selection_layout.addWidget(QLabel("结束日期:"), 1, 2)
        self.end_date_edit = QLineEdit()
        self.end_date_edit.setPlaceholderText("YYYYMMDD")
        self.end_date_edit.setMaximumWidth(100)
        data_selection_layout.addWidget(self.end_date_edit, 1, 3)
        
        # 绘图按钮区域
        button_layout = QHBoxLayout()
        raw_layout.addLayout(button_layout)
        
        # 绘图按钮
        plot_raw_btn = QPushButton("绘制原始数据图表")
        plot_raw_btn.clicked.connect(self.plot_raw_data)
        button_layout.addWidget(plot_raw_btn)
        
        # 导出按钮
        export_raw_btn = QPushButton("导出原始数据")
        export_raw_btn.clicked.connect(self.export_raw_data)
        button_layout.addWidget(export_raw_btn)
        
        self.tab_widget.addTab(self.compare_tab, "比较统计")
        self.tab_widget.addTab(self.chart_tab, "图表分析")
        
        # 状态标签
        self.status_label = QLabel("准备就绪")
        main_layout.addWidget(self.status_label)
        
        # 存储文件路径
        self.file_paths = [None] * 6  # 增加到6个文件
        self.output_dir = None
        
        # 存储比较统计结果
        self.comparison_results = []
        self.delayed_results = []  # 存储补涨补跌结果
        self.significant_results = []  # 存储显著变化结果
        self.significant_samples = []  # 存储显著变化样本数据

        # 合约上市时间（转换为整数格式：YYYYMMDD）
        self.listing_dates = {
            'IF': 20100416,  # 2010年4月16日
            'IH': 20150416,  # 2015年4月16日
            'IC': 20150416,  # 2015年4月16日
            'IM': 20220722,  # 2022年7月22日
            'SH': 19901219   # 上证指数成立日期：1990年12月19日
        }
        
        # 添加图表窗口引用
        self.chart_window = None
        
        # 存储最后一次绘制的图表数据
        self.last_comparison_figure = None
        self.last_significance_figure = None
        
        # 连接按钮信号
        self.stats_tab_export_btn.clicked.connect(lambda: self.export_statistics(self.lag_days_spin.value()))
        self.compare_export_btn.clicked.connect(self.export_comparison)
        
        # 添加样本数据导出按钮
        self.sample_export_btn = QPushButton("导出显著变化样本数据")
        self.compare_layout.addWidget(self.sample_export_btn)
        self.sample_export_btn.clicked.connect(self.export_significant_samples)

        # 存储当前原始数据
        self.current_raw_data = None
        self.current_raw_figure = None
        
        # 加载保存的配置
        self.load_config()

    def plot_and_show_significance_curve(self):
        """合并功能：绘制并显示显著性变化曲线"""
        self.plot_significance_curve()
        if self.last_significance_figure:
            self.show_significance_curve()
    
    def save_config(self):
        """保存当前配置到文件"""
        try:
            settings = QSettings("QuantAnalysis", "IncrementalDataProcessor")
            
            # 保存文件路径
            for i, path in enumerate(self.file_paths):
                settings.setValue(f"file_path_{i}", path or "")
            
            # 保存输出目录
            settings.setValue("output_dir", self.output_dir or "")
            
            # 保存参数值
            settings.setValue("lag_days", self.lag_days_spin.value())
            settings.setValue("max_lag", self.max_lag_spin.value())
            settings.setValue("std_threshold", self.std_threshold_spin.value())
            settings.setValue("quantile", self.quantile_spin.value())
            settings.setValue("threshold_method", self.threshold_method_combo.currentIndex())
            
            self.status_label.setText("配置已保存！")
            QMessageBox.information(self, "保存成功", "当前配置已保存，下次启动将自动加载")
        except Exception as e:
            QMessageBox.critical(self, "保存失败", f"保存配置时出错:\n{str(e)}")
    
    def load_config(self):
        """从文件加载配置"""
        try:
            settings = QSettings("QuantAnalysis", "IncrementalDataProcessor")
            
            # 加载文件路径
            for i in range(len(self.file_paths)):
                path = settings.value(f"file_path_{i}", "")
                if path and os.path.exists(path):
                    self.file_paths[i] = path
                    self.file_fields[i].setText(path)
            
            # 加载输出目录
            output_dir = settings.value("output_dir", "")
            if output_dir and os.path.exists(output_dir):
                self.output_dir = output_dir
                self.output_dir_edit.setText(output_dir)
            
            # 加载参数值
            self.lag_days_spin.setValue(int(settings.value("lag_days", 0)))
            self.max_lag_spin.setValue(int(settings.value("max_lag", 3)))  # 默认3天
            self.std_threshold_spin.setValue(float(settings.value("std_threshold", 1.0)))
            self.quantile_spin.setValue(float(settings.value("quantile", 90.0)))
            self.threshold_method_combo.setCurrentIndex(int(settings.value("threshold_method", 0)))
            
            self.status_label.setText("配置已加载！")
        except Exception as e:
            print(f"加载配置时出错: {str(e)}")
            self.status_label.setText("配置加载失败")

    def get_chinese_name(self, contract_type):
        """获取合约的中文名称"""
        return self.contract_name_map.get(contract_type, contract_type)
    
    def show_table(self, index):
        """显示选中的表格"""
        # 取消所有其他按钮的选择状态
        for i, btn in enumerate(self.button_group):
            btn.setChecked(i == index)
        
        # 显示对应的表格
        self.stacked_widget.setCurrentIndex(index)

    def get_raw_data(self, contract_type):
        """获取原始数据"""
        # 获取时间范围
        start_date = self.start_date_edit.text().strip()
        end_date = self.end_date_edit.text().strip()
        
        # 验证日期格式
        def is_valid_date(date_str):
            return len(date_str) == 8 and date_str.isdigit()
        
        if start_date and not is_valid_date(start_date):
            QMessageBox.warning(self, "日期错误", "开始日期格式应为YYYYMMDD")
            return None, None
            
        if end_date and not is_valid_date(end_date):
            QMessageBox.warning(self, "日期错误", "结束日期格式应为YYYYMMDD")
            return None, None
        
        try:
            # 读取净空单数据
            if not self.file_paths[0]:
                QMessageBox.warning(self, "文件错误", "净空单文件未选择")
                return None, None
            
            net_short_df = self.read_file(self.file_paths[0])
            
            # 验证净空单数据
            if contract_type == 'SH':
                # 对于上证指数，计算总净空单
                # 找出所有数值列（排除日期列）
                numeric_cols = [col for col in net_short_df.columns if col != '日期' and pd.api.types.is_numeric_dtype(net_short_df[col])]
                
                if not numeric_cols:
                    QMessageBox.warning(self, "数据错误", "净空单文件中没有可用的合约列！")
                    return None, None
                
                # 计算总净空单
                net_short_df['总净空单'] = net_short_df[numeric_cols].sum(axis=1)
            else:
                # 对于其他合约，验证是否有对应列
                if contract_type not in net_short_df.columns:
                    QMessageBox.warning(self, "数据错误", f"净空单文件中没有'{contract_type}'列！")
                    return None, None
            
            # 标准化日期格式
            net_short_df['日期'] = net_short_df['日期'].apply(self.normalize_date)
            # 过滤无效日期
            net_short_df = net_short_df[net_short_df['日期'] != ""]
            
            # 读取指数数据
            idx_map = {'IC': 1, 'IF': 2, 'IH': 3, 'IM': 4, 'SH': 5}
            idx = idx_map.get(contract_type)
            if idx is None or not self.file_paths[idx]:
                return None, None
            
            df = self.read_file(self.file_paths[idx])
            
            # 验证指数数据
            required_columns = ['日期', '收盘价']
            for col in required_columns:
                if col not in df.columns:
                    # 尝试英文列名
                    col_map = {
                        '日期': ['date', 'Date', '时间'],
                        '收盘价': ['close', 'Close', '收盘']
                    }
                    
                    found = False
                    for alt in col_map.get(col, []):
                        if alt in df.columns:
                            df = df.rename(columns={alt: col})
                            found = True
                            break
                    
                    if not found:
                        QMessageBox.warning(self, "数据错误", f"{contract_type}指数文件缺少'{col}'列！")
                        return None, None
            
            # 标准化日期格式
            df['日期'] = df['日期'].apply(self.normalize_date)
            # 过滤无效日期
            df = df[df['日期'] != ""]
            
            # 只保留日期和收盘价列
            df = df[['日期', '收盘价']]
            
            # 获取该合约的上市日期
            listing_date = self.listing_dates[contract_type]
            
            # 复制净空单数据
            if contract_type == 'SH':
                contract_df = net_short_df[['日期', '总净空单']].copy()
                contract_df = contract_df.rename(columns={'总净空单': contract_type})
            else:
                contract_df = net_short_df[['日期', contract_type]].copy()
            
            # 过滤上市日期前的数据
            contract_df = contract_df[contract_df['日期'].astype(int) >= listing_date]
            
            # 如果过滤后没有数据，返回空
            if contract_df.empty:
                return None, None
            
            # 计算净空单变化量
            contract_df['净空单变化量'] = contract_df[contract_type].diff()
            contract_df['净空单变化百分比'] = contract_df[contract_type].pct_change() * 100
            
            # 计算指数变化量
            df['指数变化量'] = df['收盘价'].diff()
            df['指数变化百分比'] = df['收盘价'].pct_change() * 100
            
            # 合并数据
            merged = pd.merge(contract_df, df, on='日期', how='left')
            
            # 按日期排序
            merged = merged.sort_values('日期')
            
            # 如果过滤后没有数据，返回空
            if merged.empty:
                return None, None
            
            return merged
        
        except Exception as e:
            QMessageBox.critical(self, "加载错误", f"加载原始数据时出错:\n{str(e)}")
            return None, None

    def plot_raw_data(self):
        """绘制原始数据图表"""
        # 获取合约类型
        contract_type = self.contract_combo.currentData()
        chinese_name = self.get_chinese_name(contract_type)
        
        # 获取显示类型
        display_type = self.display_type_combo.currentText()
        
        # 获取数据
        net_short_data, index_data = self.get_raw_data(contract_type)
        
        if net_short_data is None or index_data is None or net_short_data.empty or index_data.empty:
            QMessageBox.warning(self, "数据错误", f"无法获取{chinese_name}的净空单或指数数据！")
            return
            
        # 保存数据供导出
        self.current_raw_data = {
            'net_short': net_short_data,
            'index': index_data
        }
        
        # 创建新的图表对象
        figure = plt.figure(figsize=(14, 10), dpi=100)
        
        # 设置支持中文的字体
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Zen Hei']
        plt.rcParams['axes.unicode_minus'] = False
        
        # 创建两个子图（上下排列，共享X轴）
        ax1 = figure.add_subplot(211)
        ax2 = figure.add_subplot(212, sharex=ax1)
        
        # 设置主标题
        start_date = net_short_data['日期'].min()
        end_date = net_short_data['日期'].max()
        figure.suptitle(f"{chinese_name}净空单与指数关系\n({start_date} 至 {end_date})", fontsize=14)
        
        # 绘制净空单数据（上方子图）
        if display_type == "实际数据":
            # 绘制曲线图
            ax1.plot(range(len(net_short_data)), net_short_data['净空单'], 'b-', linewidth=2, label='净空单')
            ax1.set_ylabel('净空单', fontsize=12)
        else:  # 变化量
            # 绘制柱状图
            colors = ['g' if x >= 0 else 'r' for x in net_short_data['净空单变化量']]
            ax1.bar(range(len(net_short_data)), net_short_data['净空单变化量'], color=colors)
            ax1.axhline(y=0, color='k', linestyle='-', alpha=0.3)
            ax1.set_ylabel('净空单变化量', fontsize=12)
        
        # 绘制指数数据（下方子图）
        if display_type == "实际数据":
            # 绘制曲线图
            ax2.plot(range(len(index_data)), index_data['指数'], 'r-', linewidth=2, label='指数')
            ax2.set_ylabel('指数', fontsize=12)
        else:  # 变化量
            # 绘制柱状图
            colors = ['g' if x >= 0 else 'r' for x in index_data['指数变化量']]
            ax2.bar(range(len(index_data)), index_data['指数变化量'], color=colors)
            ax2.axhline(y=0, color='k', linestyle='-', alpha=0.3)
            ax2.set_ylabel('指数变化量', fontsize=12)
        
        # 设置X轴标签
        ax2.set_xlabel('时间序列', fontsize=12)
        
        # 设置X轴刻度格式
        date_labels = [str(int(date)) for date in net_short_data['日期']]
        
        # 如果日期太多，只显示部分标签
        if len(date_labels) > 20:
            step = max(1, len(date_labels) // 10)
            positions = list(range(0, len(date_labels), step))
            labels = date_labels[::step]
            ax2.set_xticks(positions)
            ax2.set_xticklabels(labels, rotation=45, ha='right')
        else:
            ax2.set_xticks(range(len(net_short_data)))
            ax2.set_xticklabels(date_labels, rotation=45, ha='right')
        
        # 添加网格和图例
        ax1.grid(True, linestyle='--', alpha=0.6)
        ax2.grid(True, linestyle='--', alpha=0.6)
        ax1.legend(loc='best', fontsize=10)
        ax2.legend(loc='best', fontsize=10)
        
        # 调整布局
        figure.tight_layout()
        figure.subplots_adjust(top=0.9)  # 为总标题留出空间
        
        # 保存图表对象
        self.current_raw_figure = figure

        # 创建或激活图表窗口
        if not self.chart_window:
            self.chart_window = self.ChartWindow(self)
        else:
            self.chart_window.figure.clear()
            
        # 将图表复制到新窗口
        self.chart_window.figure = figure
        self.chart_window.canvas.figure = figure
        self.chart_window.canvas.draw()
        
        # 显示窗口
        self.chart_window.show()
        self.chart_window.raise_()  # 置于顶层

    def export_raw_data(self):
        """导出原始数据到CSV"""
        if self.current_raw_data is None or not self.output_dir:
            QMessageBox.warning(self, "无数据", "没有可导出的原始数据或未选择输出目录")
            return
            
        # 获取合约中文名称
        contract_type = self.contract_combo.currentData()
        chinese_name = self.get_chinese_name(contract_type)
        
        # 生成文件名
        net_short_data = self.current_raw_data['net_short']
        index_data = self.current_raw_data['index']
        start_date = net_short_data['日期'].min()
        end_date = net_short_data['日期'].max()
        
        # 保存净空单数据
        net_short_path = os.path.join(self.output_dir, f"{chinese_name}_净空单数据_{start_date}_{end_date}.csv")
        net_short_data.to_csv(net_short_path, index=False, float_format='%.4f')
        
        # 保存指数数据
        index_path = os.path.join(self.output_dir, f"{chinese_name}_指数数据_{start_date}_{end_date}.csv")
        index_data.to_csv(index_path, index=False, float_format='%.4f')
        
        QMessageBox.information(self, "导出成功", f"原始数据已导出到:\n{net_short_path}\n{index_path}")
    
    def create_browse_handler(self, line_edit):
        def handler():
            idx = self.file_fields.index(line_edit)
            file_path, _ = QFileDialog.getOpenFileName(
                self, 
                f"选择{['净空单', '中证500', '沪深300', '上证50', '中证1000', '上证指数'][idx]}文件",
                "", 
                "CSV文件 (*.csv);;Excel文件 (*.xlsx);;所有文件 (*)"
            )
            if file_path:
                line_edit.setText(file_path)
                self.file_paths[idx] = file_path
                # 预览文件前5行
                try:
                    if file_path.endswith(('.xlsx', '.xls')):
                        preview_df = pd.read_excel(file_path, nrows=5)
                    else:
                        # 尝试不同编码
                        encodings = ['utf-8', 'gbk', 'latin1']
                        for enc in encodings:
                            try:
                                preview_df = pd.read_csv(file_path, encoding=enc, nrows=5)
                                break
                            except:
                                continue
                        else:
                            preview_df = pd.read_csv(file_path, encoding='utf-8', errors='replace', nrows=5)
                    
                    preview_text = f"文件预览 (前5行):\n\n{preview_df.to_string()}"
                    QMessageBox.information(self, "文件预览", preview_text)
                except Exception as e:
                    QMessageBox.warning(self, "预览失败", f"无法预览文件:\n{str(e)}")
        return handler
    
    def browse_output_dir(self):
        dir_path = QFileDialog.getExistingDirectory(self, "选择输出目录")
        if dir_path:
            self.output_dir_edit.setText(dir_path)
            self.output_dir = dir_path
    
    def normalize_date(self, date_str):
        """更健壮的日期格式处理"""
        if pd.isna(date_str):
            return ""
        
        try:
            # 尝试直接解析为日期对象
            if isinstance(date_str, (int, float)):
                # 处理数值型日期（如20220101）
                date_str = str(int(date_str))
            
            if isinstance(date_str, str):
                # 处理多种分隔符
                date_str = re.sub(r'[^0-9/]', '', date_str)  # 允许保留斜杠
                
                # 处理YYYY/M/D格式
                if re.match(r'^\d{4}/\d{1,2}/\d{1,2}$', date_str):
                    parts = date_str.split('/')
                    year = parts[0]
                    month = parts[1].zfill(2)  # 补零
                    day = parts[2].zfill(2)     # 补零
                    return year + month + day
                
                # 处理其他格式
                date_str = re.sub(r'[^0-9]', '', date_str)  # 移除非数字字符
                
                # 处理不同长度的日期
                if len(date_str) == 8:  # YYYYMMDD
                    return date_str
                elif len(date_str) == 6:  # YYYYMM
                    return date_str + "01"
                elif len(date_str) == 4:  # YYYY
                    return date_str + "0101"
            
            # 尝试解析为日期对象
            parsed_date = pd.to_datetime(date_str, errors='coerce', format='mixed')
            if not pd.isna(parsed_date):
                return parsed_date.strftime('%Y%m%d')
            return ""
        except Exception as e:
            print(f"日期解析错误: {str(e)}")
            return ""
    
    def debug_show_data_info(self, df, name):
        """调试用：显示数据框信息"""
        if df.empty:
            return f"{name}数据为空"
        
        info = f"{name}数据信息:\n"
        info += f"行数: {len(df)}\n"
        info += f"日期范围: {df['日期'].min()} 至 {df['日期'].max()}\n" if '日期' in df.columns else "无日期列\n"
        info += f"列: {', '.join(df.columns)}\n"
        info += f"前3行:\n{df.head(3).to_string()}\n"
        return info

    def validate_data(self, df, expected_columns):
        """验证数据框是否包含必需列"""
        missing = [col for col in expected_columns if col not in df.columns]
        if missing:
            raise ValueError(f"文件缺少必需列: {', '.join(missing)}")
        return True
    
    def calculate_probabilities(self, df, contract_type, lag_days):
        """计算净空单增减情况下指数增减的概率和平均涨幅（百分比）"""
        stats = {}
        
        # 净空单变化量分类
        net_short_up = df[df['净空单变化量'] > 0]
        net_short_down = df[df['净空单变化量'] < 0]
        
        # 指数变化量分类
        index_up = df[df['指数变化量'] > 0]
        index_down = df[df['指数变化量'] < 0]
        
        # 计算各种组合
        both_up = df[(df['净空单变化量'] > 0) & (df['指数变化量'] > 0)]
        both_down = df[(df['净空单变化量'] < 0) & (df['指数变化量'] < 0)]
        net_short_up_index_down = df[(df['净空单变化量'] > 0) & (df['指数变化量'] < 0)]
        net_short_down_index_up = df[(df['净空单变化量'] < 0) & (df['指数变化量'] > 0)]
        
        # 计算概率
        total = len(df)
        if total > 0:
            stats['总样本数'] = total
            stats['净空单增加次数'] = len(net_short_up)
            stats['净空单减少次数'] = len(net_short_down)
            stats['指数增加次数'] = len(index_up)
            stats['指数减少次数'] = len(index_down)
            
            # 条件概率
            if len(net_short_up) > 0:
                stats['看跌概率'] = len(net_short_up_index_down) / len(net_short_up)  # 指数减少 | 净空单增加
                # 计算平均涨幅（百分比）- 使用指数变化百分比
                stats['净空单增加平均涨幅'] = net_short_up['指数变化百分比'].mean()
            else:
                stats['看跌概率'] = np.nan
                stats['净空单增加平均涨幅'] = np.nan
                
            if len(net_short_down) > 0:
                stats['看涨概率'] = len(net_short_down_index_up) / len(net_short_down)  # 指数增加 | 净空单减少
                # 计算平均涨幅（百分比）- 使用指数变化百分比
                stats['净空单减少平均涨幅'] = net_short_down['指数变化百分比'].mean()
            else:
                stats['看涨概率'] = np.nan
                stats['净空单减少平均涨幅'] = np.nan
        
        return stats
    
    def calculate_delayed_probabilities(self, df, contract_type, max_lag):
        """计算补涨/补跌的条件概率（从滞后第2天开始计算）"""
        results = []
        
        # 净空单增加的样本（用于补跌计算）
        net_short_up = df[df['净空单变化量'] > 0]
        
        # 净空单减少的样本（用于补涨计算）
        net_short_down = df[df['净空单变化量'] < 0]
        
        # 创建副本用于延迟计算
        df_up = net_short_up.copy()
        df_down = net_short_down.copy()
        
        # 计算延迟下跌概率（补跌）
        for k in range(2, max_lag + 1):  # 从滞后第2天开始计算
            # 创建延迟列
            for i in range(1, k + 1):
                df_up[f'指数变化量_延迟{i}天'] = df_up['指数变化量'].shift(-i)
                df_up[f'指数变化百分比_延迟{i}天'] = df_up['指数变化百分比'].shift(-i)
            
            # 条件1：前k-1天没有下跌（>=0）
            no_drop_conditions = []
            for i in range(1, k):
                no_drop_conditions.append(df_up[f'指数变化量_延迟{i}天'] >= 0)
            
            # 条件2：第k天下跌（<0）
            drop_condition = df_up[f'指数变化量_延迟{k}天'] < 0
            
            # 组合条件
            if no_drop_conditions:
                no_drop_condition = np.logical_and.reduce(no_drop_conditions)
                condition = no_drop_condition & drop_condition
            else:
                condition = drop_condition
            
            # 符合条件的样本数
            delayed_count = condition.sum()
            
            # 分母是前k-1天没有下跌的样本数
            total_count = no_drop_condition.sum() if no_drop_conditions else len(df_up)
            
            # 计算概率
            if total_count > 0:
                delayed_prob = delayed_count / total_count
                # 计算平均跌幅（百分比）
                avg_drop_pct = df_up[condition][f'指数变化百分比_延迟{k}天'].mean()
            else:
                delayed_prob = np.nan
                avg_drop_pct = np.nan
            
            # 保存结果
            results.append({
                '合约': contract_type,
                '类型': '补跌',
                '延迟天数': k,
                '概率': delayed_prob,
                '平均涨幅': avg_drop_pct,
                '满足条件样本数': delayed_count,
                '总样本数': total_count,
                '条件描述': f"T+1~T+{k-1}天未下跌, T+{k}天下跌"
            })
        
        # 计算延迟上涨概率（补涨）
        for k in range(2, max_lag + 1):  # 从滞后第2天开始计算
            # 创建延迟列
            for i in range(1, k + 1):
                df_down[f'指数变化量_延迟{i}天'] = df_down['指数变化量'].shift(-i)
                df_down[f'指数变化百分比_延迟{i}天'] = df_down['指数变化百分比'].shift(-i)
            
            # 条件1：前k-1天没有上涨（<=0）
            no_rise_conditions = []
            for i in range(1, k):
                no_rise_conditions.append(df_down[f'指数变化量_延迟{i}天'] <= 0)
            
            # 条件2：第k天上涨（>0）
            rise_condition = df_down[f'指数变化量_延迟{k}天'] > 0
            
            # 组合条件
            if no_rise_conditions:
                no_rise_condition = np.logical_and.reduce(no_rise_conditions)
                condition = no_rise_condition & rise_condition
            else:
                condition = rise_condition
            
            # 符合条件的样本数
            delayed_count = condition.sum()
            
            # 分母是前k-1天没有上涨的样本数
            total_count = no_rise_condition.sum() if no_rise_conditions else len(df_down)
            
            # 计算概率
            if total_count > 0:
                delayed_prob = delayed_count / total_count
                # 计算平均涨幅（百分比）
                avg_rise_pct = df_down[condition][f'指数变化百分比_延迟{k}天'].mean()
            else:
                delayed_prob = np.nan
                avg_rise_pct = np.nan
            
            # 保存结果
            results.append({
                '合约': contract_type,
                '类型': '补涨',
                '延迟天数': k,
                '概率': delayed_prob,
                '平均涨幅': avg_rise_pct,
                '满足条件样本数': delayed_count,
                '总样本数': total_count,
                '条件描述': f"T+1~T+{k-1}天未上涨, T+{k}天上涨"
            })
        
        return results
    
    def analyze_significant_change(self, df, contract_type, std_multiplier=1.0, max_lag=5, 
                              threshold_method="both", quantile=90.0):
        """分析净空单显著变化（增大或减小）对指数的影响，并返回结果和样本数据"""
        results = []
        samples = []  # 存储样本数据
        
        # 0. 参数校验
        if threshold_method not in ["fixed", "rolling", "quantile", "both", "all"]:
            threshold_method = "both"  # 默认两者都使用
        
        # 1. 为整个数据集添加滞后列（T+1到T+max_lag天）
        for k in range(1, max_lag + 1):
            df[f'指数变化量_延迟{k}天'] = df['指数变化量'].shift(-k)
            df[f'指数变化百分比_延迟{k}天'] = df['指数变化百分比'].shift(-k)
        
        # 2. 分离正值（增加）和负值（减少）的变化量
        # 只考虑有实际变化的数据（排除变化量为0的情况）
        positive_changes = df[df['净空单变化量'] > 0].copy()
        negative_changes = df[df['净空单变化量'] < 0].copy()
        
        # 3. 方法1: 固定阈值法（当选择固定或两者或所有时执行）
        if threshold_method in ["fixed", "both", "all"]:
            # 计算正值（增加）的变化量的均值和标准差
            if not positive_changes.empty:
                pos_mean = positive_changes['净空单变化量'].mean()
                pos_std = positive_changes['净空单变化量'].std()
                increase_threshold = pos_mean + std_multiplier * pos_std
                
                # 筛选显著增大的样本（只从正值变化中筛选）
                significant_increase = positive_changes[positive_changes['净空单变化量'] > increase_threshold].copy()
                
                # 分析显著增大（看跌）
                if not significant_increase.empty:
                    for k in range(1, max_lag + 1):
                        pct_col_name = f'指数变化百分比_延迟{k}天'
                        
                        # 计算平均变化量百分比（不区分涨跌）
                        avg_change_pct = significant_increase[pct_col_name].mean()
                        
                        # 计算下跌概率
                        drop_condition = significant_increase[f'指数变化量_延迟{k}天'] < 0
                        drop_count = drop_condition.sum()
                        total_count = len(significant_increase) - significant_increase[f'指数变化量_延迟{k}天'].isna().sum()
                        prob = drop_count / total_count if total_count > 0 else np.nan
                        
                        results.append({
                            '合约': contract_type,
                            '变化类型': '显著增大',
                            '方向': '正值',
                            '滞后天数': k,
                            '平均变化量百分比': avg_change_pct,
                            '概率': prob,  # 添加概率值
                            '满足条件样本数': total_count,
                            '变化描述': '下跌' if avg_change_pct < 0 else '上涨',
                            '方法': '固定阈值法',
                            '标准差倍数': std_multiplier,
                            '净空单均值': pos_mean,
                            '净空单标准差': pos_std,
                            '阈值': increase_threshold
                        })
                    
                    # 收集样本数据
                    sample_df = significant_increase.copy()
                    sample_df['变化类型'] = '显著增大'
                    sample_df['合约'] = contract_type
                    sample_df['方法'] = '固定阈值法'
                    sample_df['阈值'] = increase_threshold
                    samples.append(sample_df)
        
            # 计算负值（减少）的变化量的均值和标准差
            if not negative_changes.empty:
                neg_mean = negative_changes['净空单变化量'].mean()
                neg_std = negative_changes['净空单变化量'].std()
                decrease_threshold = neg_mean - std_multiplier * neg_std  # 注意这里是减法
                
                # 筛选显著减小的样本（只从负值变化中筛选）
                significant_decrease = negative_changes[negative_changes['净空单变化量'] < decrease_threshold].copy()
                
                # 分析显著减小（看涨）
                if not significant_decrease.empty:
                    for k in range(1, max_lag + 1):
                        pct_col_name = f'指数变化百分比_延迟{k}天'
                        
                        # 计算平均变化量百分比（不区分涨跌）
                        avg_change_pct = significant_decrease[pct_col_name].mean()
                        
                        # 计算上涨概率
                        rise_condition = significant_decrease[f'指数变化量_延迟{k}天'] > 0
                        rise_count = rise_condition.sum()
                        total_count = len(significant_decrease) - significant_decrease[f'指数变化量_延迟{k}天'].isna().sum()
                        prob = rise_count / total_count if total_count > 0 else np.nan
                        
                        results.append({
                            '合约': contract_type,
                            '变化类型': '显著减小',
                            '方向': '负值',
                            '滞后天数': k,
                            '平均变化量百分比': avg_change_pct,
                            '概率': prob,  # 添加概率值
                            '满足条件样本数': total_count,
                            '变化描述': '上涨' if avg_change_pct > 0 else '下跌',
                            '方法': '固定阈值法',
                            '标准差倍数': std_multiplier,
                            '净空单均值': neg_mean,
                            '净空单标准差': neg_std,
                            '阈值': decrease_threshold
                        })
                    
                    # 收集样本数据
                    sample_df = significant_decrease.copy()
                    sample_df['变化类型'] = '固定阈值_显著减小'
                    sample_df['合约'] = contract_type
                    sample_df['方法'] = '固定阈值法'
                    sample_df['阈值'] = decrease_threshold
                    samples.append(sample_df)

        # 4. 方法2: 滚动阈值法（当选择滚动或两者或所有时执行）
        if threshold_method in ["rolling", "both", "all"]:
            # 创建滚动窗口统计函数（正负值分开）
            def rolling_stats(series, window=20, min_periods=10):
                """计算滚动窗口的均值和标准差（正负值分开）"""
                # 正值滚动统计
                pos_series = series.copy()
                pos_series[pos_series <= 0] = np.nan  # 只保留正值
                pos_mean = pos_series.rolling(window=window, min_periods=min_periods).mean()
                pos_std = pos_series.rolling(window=window, min_periods=min_periods).std()
                
                # 负值滚动统计
                neg_series = series.copy()
                neg_series[neg_series >= 0] = np.nan  # 只保留负值
                neg_mean = neg_series.rolling(window=window, min_periods=min_periods).mean()
                neg_std = neg_series.rolling(window=window, min_periods=min_periods).std()
                
                return pos_mean, pos_std, neg_mean, neg_std
            
            # 计算滚动统计量（使用前一天的数据）
            # 注意: 使用shift(1)确保不包含当天数据
            df['rolling_pos_mean'], df['rolling_pos_std'], df['rolling_neg_mean'], df['rolling_neg_std'] = rolling_stats(
                df['净空单变化量'].shift(1), window=20, min_periods=10
            )
            
            # 删除前20行（无滚动数据）
            df_rolling = df.dropna(subset=['rolling_pos_mean', 'rolling_pos_std', 'rolling_neg_mean', 'rolling_neg_std']).copy()
            
            if not df_rolling.empty:
                # 计算动态阈值
                increase_threshold_rolling = df_rolling['rolling_pos_mean'] + std_multiplier * df_rolling['rolling_pos_std']
                decrease_threshold_rolling = df_rolling['rolling_neg_mean'] - std_multiplier * df_rolling['rolling_neg_std']
                
                # 筛选显著增大的样本（滚动阈值法，正值）
                significant_increase_rolling = df_rolling[
                    (df_rolling['净空单变化量'] > 0) &  # 必须是正值
                    (df_rolling['净空单变化量'] > increase_threshold_rolling)
                ].copy()
                
                # 筛选显著减小的样本（滚动阈值法，负值）
                significant_decrease_rolling = df_rolling[
                    (df_rolling['净空单变化量'] < 0) &  # 必须是负值
                    (df_rolling['净空单变化量'] < decrease_threshold_rolling)
                ].copy()
                
                # 分析显著增大（看跌）- 滚动阈值法
                if not significant_increase_rolling.empty:
                    for k in range(1, max_lag + 1):
                        pct_col_name = f'指数变化百分比_延迟{k}天'
                        
                        # 计算平均变化量百分比（不区分涨跌）
                        avg_change_pct = significant_increase_rolling[pct_col_name].mean()
                        
                        # 计算下跌概率
                        drop_condition = significant_increase_rolling[f'指数变化量_延迟{k}天'] < 0
                        drop_count = drop_condition.sum()
                        total_count = len(significant_increase_rolling) - significant_increase_rolling[f'指数变化量_延迟{k}天'].isna().sum()
                        prob = drop_count / total_count if total_count > 0 else np.nan
                        
                        # 计算平均滚动统计量
                        avg_rolling_pos_mean = significant_increase_rolling['rolling_pos_mean'].mean()
                        avg_rolling_pos_std = significant_increase_rolling['rolling_pos_std'].mean()
                        avg_threshold = significant_increase_rolling['rolling_pos_mean'] + std_multiplier * significant_increase_rolling['rolling_pos_std']
                        avg_threshold = avg_threshold.mean()
                        
                        results.append({
                            '合约': contract_type,
                            '变化类型': '显著增大',
                            '方向': '正值',
                            '滞后天数': k,
                            '平均变化量百分比': avg_change_pct,
                            '概率': prob,  # 添加概率值
                            '满足条件样本数': len(significant_increase_rolling),
                            '变化描述': '下跌' if avg_change_pct < 0 else '上涨',
                            '方法': '滚动阈值法',
                            '标准差倍数': std_multiplier,
                            '滚动窗口': 20,
                            '滚动均值': avg_rolling_pos_mean,
                            '滚动标准差': avg_rolling_pos_std,
                            '阈值': avg_threshold
                        })
                    
                    # 收集样本数据
                    sample_df = significant_increase_rolling.copy()
                    sample_df['变化类型'] = '滚动阈值_显著增大'
                    sample_df['合约'] = contract_type
                    sample_df['方法'] = '滚动阈值法'
                    sample_df['阈值'] = increase_threshold_rolling
                    samples.append(sample_df)
                    
                # 分析显著减小（看涨）- 滚动阈值法
                if not significant_decrease_rolling.empty:
                    for k in range(1, max_lag + 1):
                        pct_col_name = f'指数变化百分比_延迟{k}天'
                        
                        # 计算平均变化量百分比（不区分涨跌）
                        avg_change_pct = significant_decrease_rolling[pct_col_name].mean()
                        
                        # 计算上涨概率
                        rise_condition = significant_decrease_rolling[f'指数变化量_延迟{k}天'] > 0
                        rise_count = rise_condition.sum()
                        total_count = len(significant_decrease_rolling) - significant_decrease_rolling[f'指数变化量_延迟{k}天'].isna().sum()
                        prob = rise_count / total_count if total_count > 0 else np.nan
                        
                        # 计算平均滚动统计量
                        avg_rolling_neg_mean = significant_decrease_rolling['rolling_neg_mean'].mean()
                        avg_rolling_neg_std = significant_decrease_rolling['rolling_neg_std'].mean()
                        avg_threshold = significant_decrease_rolling['rolling_neg_mean'] - std_multiplier * significant_decrease_rolling['rolling_neg_std']
                        avg_threshold = avg_threshold.mean()
                        
                        results.append({
                            '合约': contract_type,
                            '变化类型': '显著减小',
                            '方向': '负值',
                            '滞后天数': k,
                            '平均变化量百分比': avg_change_pct,
                            '概率': prob,  # 添加概率值
                            '满足条件样本数': len(significant_decrease_rolling),
                            '变化描述': '上涨' if avg_change_pct > 0 else '下跌',
                            '方法': '滚动阈值法',
                            '标准差倍数': std_multiplier,
                            '滚动窗口': 20,
                            '滚动均值': avg_rolling_neg_mean,
                            '滚动标准差': avg_rolling_neg_std,
                            '阈值': avg_threshold
                        })
                    
                    # 收集样本数据
                    sample_df = significant_decrease_rolling.copy()
                    sample_df['变化类型'] = '滚动阈值_显著减小'
                    sample_df['合约'] = contract_type
                    sample_df['方法'] = '滚动阈值法'
                    sample_df['阈值'] = decrease_threshold_rolling
                    samples.append(sample_df)

        # 5. 方法3: 分位数阈值法（当选择分位数或所有时执行）
        if threshold_method in ["quantile", "all"]:
            # 复制数据
            df_quantile = df.copy()
            
            # 分离正值和负值
            positive_changes = df_quantile[df_quantile['净空单变化量'] > 0]
            negative_changes = df_quantile[df_quantile['净空单变化量'] < 0]
            
            # 初始化变量，避免未定义错误
            significant_increase_quantile = pd.DataFrame()
            significant_decrease_quantile = pd.DataFrame()
            
            # 计算分位数阈值
            if not positive_changes.empty:
                # 正值变化量取上分位数（例如90%）
                increase_threshold_quantile = np.percentile(
                    positive_changes['净空单变化量'], quantile
                )
                # 筛选显著增大的样本（分位数阈值法，正值）
                significant_increase_quantile = positive_changes[
                    positive_changes['净空单变化量'] > increase_threshold_quantile
                ].copy()
            else:
                increase_threshold_quantile = np.nan
                    
            if not negative_changes.empty:
                # 负值变化量取下分位数（例如10%）
                decrease_threshold_quantile = np.percentile(
                    negative_changes['净空单变化量'], 100 - quantile
                )
                # 筛选显著减小的样本（分位数阈值法，负值）
                significant_decrease_quantile = negative_changes[
                    negative_changes['净空单变化量'] < decrease_threshold_quantile
                ].copy()
            else:
                decrease_threshold_quantile = np.nan
                    
            # 分析显著增大（看跌）- 分位数阈值法
            if not significant_increase_quantile.empty:
                for k in range(1, max_lag + 1):
                    pct_col_name = f'指数变化百分比_延迟{k}天'
                    
                    # 计算平均变化量百分比（不区分涨跌）
                    avg_change_pct = significant_increase_quantile[pct_col_name].mean()
                    
                    # 计算下跌概率
                    drop_condition = significant_increase_quantile[f'指数变化量_延迟{k}天'] < 0
                    drop_count = drop_condition.sum()
                    total_count = len(significant_increase_quantile) - significant_increase_quantile[f'指数变化量_延迟{k}天'].isna().sum()
                    prob = drop_count / total_count if total_count > 0 else np.nan
                    
                    results.append({
                        '合约': contract_type,
                        '变化类型': '显著增大',
                        '方向': '正值',
                        '滞后天数': k,
                        '平均变化量百分比': avg_change_pct,
                        '概率': prob,  # 添加概率值
                        '满足条件样本数': len(significant_increase_quantile),
                        '变化描述': '下跌' if avg_change_pct < 0 else '上涨',
                        '方法': '分位数阈值法',
                        '分位数': quantile,
                        '阈值': increase_threshold_quantile
                    })
                
                # 收集样本数据
                sample_df = significant_increase_quantile.copy()
                sample_df['变化类型'] = '分位数阈值_显著增大'
                sample_df['合约'] = contract_type
                sample_df['方法'] = '分位数阈值法'
                sample_df['阈值'] = increase_threshold_quantile
                samples.append(sample_df)
                    
            # 分析显著减小（看涨）- 分位数阈值法
            if not significant_decrease_quantile.empty:
                for k in range(1, max_lag + 1):
                    pct_col_name = f'指数变化百分比_延迟{k}天'
                    
                    # 计算平均变化量百分比（不区分涨跌）
                    avg_change_pct = significant_decrease_quantile[pct_col_name].mean()
                    
                    # 计算上涨概率
                    rise_condition = significant_decrease_quantile[f'指数变化量_延迟{k}天'] > 0
                    rise_count = rise_condition.sum()
                    total_count = len(significant_decrease_quantile) - significant_decrease_quantile[f'指数变化量_延迟{k}天'].isna().sum()
                    prob = rise_count / total_count if total_count > 0 else np.nan
                    
                    results.append({
                        '合约': contract_type,
                        '变化类型': '显著减小',
                        '方向': '负值',
                        '滞后天数': k,
                        '平均变化量百分比': avg_change_pct,
                        '概率': prob,  # 添加概率值
                        '满足条件样本数': len(significant_decrease_quantile),
                        '变化描述': '上涨' if avg_change_pct > 0 else '下跌',
                        '方法': '分位数阈值法',
                        '分位数': quantile,
                        '阈值': decrease_threshold_quantile
                    })
                
                # 收集样本数据
                sample_df = significant_decrease_quantile.copy()
                sample_df['变化类型'] = '分位数阈值_显著减小'
                sample_df['合约'] = contract_type
                sample_df['方法'] = '分位数阈值法'
                sample_df['阈值'] = decrease_threshold_quantile
                samples.append(sample_df)
        
        return results, samples
    
    def read_file(self, path):
        """统一文件读取方式，支持多种格式和编码"""
        if path.endswith(('.xlsx', '.xls')):
            return pd.read_excel(path)
        else:  # 默认按CSV处理
            # 尝试不同编码
            encodings = ['utf-8', 'gbk', 'latin1']
            for enc in encodings:
                try:
                    return pd.read_csv(path, encoding=enc)
                except:
                    continue
            # 如果所有编码都失败，使用错误替换策略
            return pd.read_csv(path, encoding='utf-8', errors='replace')
    
    def process_contract_data(self, contract_type, lag_days):
        """处理单个合约的数据"""
        try:
            # 读取净空单数据
            if not self.file_paths[0]:
                return pd.DataFrame()
            
            net_short_df = self.read_file(self.file_paths[0])
            
            # 验证净空单数据
            if contract_type == 'SH':
                # 对于上证指数，计算总净空单
                # 找出所有数值列（排除日期列）
                numeric_cols = [col for col in net_short_df.columns if col != '日期' and pd.api.types.is_numeric_dtype(net_short_df[col])]
                
                if not numeric_cols:
                    self.stats_text.append("错误: 净空单文件中没有可用的合约列！")
                    return pd.DataFrame()
                
                # 计算总净空单
                net_short_df['总净空单'] = net_short_df[numeric_cols].sum(axis=1)
            else:
                # 对于其他合约，验证是否有对应列
                if contract_type not in net_short_df.columns:
                    self.stats_text.append(f"错误: 净空单文件中没有'{contract_type}'列！")
                    return pd.DataFrame()
            
            # 标准化日期格式
            net_short_df['日期'] = net_short_df['日期'].apply(self.normalize_date)
            # 过滤无效日期
            net_short_df = net_short_df[net_short_df['日期'] != ""]
            
            # 调试信息
            debug_info = self.debug_show_data_info(net_short_df, "净空单")
            self.stats_text.append(debug_info)
            
            # 读取指数数据
            idx_map = {'IC': 1, 'IF': 2, 'IH': 3, 'IM': 4, 'SH': 5}
            idx = idx_map.get(contract_type)
            if idx is None or not self.file_paths[idx]:
                return pd.DataFrame()
            
            df = self.read_file(self.file_paths[idx])
            
            # 验证指数数据
            required_columns = ['日期', '收盘价']
            for col in required_columns:
                if col not in df.columns:
                    # 尝试英文列名
                    col_map = {
                        '日期': ['date', 'Date', '时间'],
                        '收盘价': ['close', 'Close', '收盘']
                    }
                    
                    found = False
                    for alt in col_map.get(col, []):
                        if alt in df.columns:
                            df = df.rename(columns={alt: col})
                            found = True
                            break
                    
                    if not found:
                        self.stats_text.append(f"错误: {contract_type}指数文件缺少'{col}'列！")
                        return pd.DataFrame()
            
            # 标准化日期格式
            df['日期'] = df['日期'].apply(self.normalize_date)
            # 过滤无效日期
            df = df[df['日期'] != ""]
            
            # 调试信息
            debug_info = self.debug_show_data_info(df, "指数")
            self.stats_text.append(debug_info)
            
            # 只保留日期和收盘价列
            df = df[['日期', '收盘价']]
            
            # 获取该合约的上市日期
            listing_date = self.listing_dates[contract_type]
            
            # 复制净空单数据
            if contract_type == 'SH':
                contract_df = net_short_df[['日期', '总净空单']].copy()
                contract_df = contract_df.rename(columns={'总净空单': contract_type})
            else:
                contract_df = net_short_df[['日期', contract_type]].copy()
            
            # 过滤上市日期前的数据
            contract_df = contract_df[contract_df['日期'].astype(int) >= listing_date]
            
            # 如果过滤后没有数据，返回空
            if contract_df.empty:
                self.stats_text.append(f"警告: {contract_type}合约在上市日期({listing_date})之后没有数据！")
                return pd.DataFrame()
            
            # 计算净空单变化量
            contract_df['净空单变化量'] = contract_df[contract_type].diff()
            contract_df['净空单变化百分比'] = contract_df[contract_type].pct_change() * 100
            
            # 计算指数变化量
            df['指数变化量'] = df['收盘价'].diff()
            df['指数变化百分比'] = df['收盘价'].pct_change() * 100
            
            # 合并数据
            merged = pd.merge(contract_df, df, on='日期', how='left')
            
            # 按日期排序
            merged = merged.sort_values('日期')
            
            # 应用滞后效应：将指数变化量向前移动lag_days天
            if lag_days > 0:
                # 移动指数变化量列
                merged['指数变化量'] = merged['指数变化量'].shift(-lag_days)
                merged['指数变化百分比'] = merged['指数变化百分比'].shift(-lag_days)
                
                # 丢弃最后lag_days行（这些行没有对应的未来指数数据）
                if len(merged) > lag_days:
                    merged = merged.iloc[:-lag_days]
                else:
                    # 如果数据长度小于滞后天数，则清空数据
                    self.stats_text.append(f"警告: {contract_type}滞后{lag_days}天数据不足！")
                    merged = merged.iloc[0:0]
            
            # 删除包含NaN的行（由于移动和变化量计算）
            if not merged.empty:
                merged = merged.dropna()
            
            # 调试信息
            if merged.empty:
                self.stats_text.append(f"警告: {contract_type}滞后{lag_days}天合并后无数据！")
                self.stats_text.append("可能原因: 日期格式不一致、数据范围不重叠或滞后天数太大")
            else:
                self.stats_text.append(f"{contract_type}滞后{lag_days}天有效数据量: {len(merged)}行")
                self.stats_text.append(f"日期范围: {merged['日期'].min()} 至 {merged['日期'].max()}")
            
            return merged
        
        except Exception as e:
            self.stats_text.append(f"处理{contract_type}数据时出错: {str(e)}")
            self.stats_text.append(traceback.format_exc())
            return pd.DataFrame()
    
    def process_data(self):
        # 检查所有文件是否已选择
        if None in self.file_paths or self.output_dir is None:
            QMessageBox.warning(self, "警告", "请选择所有输入文件和输出目录！")
            return
        
        try:
            self.status_label.setText("正在处理数据...")
            self.stats_text.clear()
            QApplication.processEvents()  # 更新UI
            
            # 获取T+N天数参数
            lag_days = self.lag_days_spin.value()
            
            # 存储所有统计结果
            all_stats = []
            
            # 处理每种指数类型
            contract_types = ['IC', 'IF', 'IH', 'IM', 'SH']  # 新增SH合约
            
            for contract_type in contract_types:
                chinese_name = self.get_chinese_name(contract_type)
                # 处理数据
                self.stats_text.append(f"\n处理 {chinese_name} 指数...")
                merged = self.process_contract_data(contract_type, lag_days)
                
                if merged.empty:
                    if contract_type == 'SH':
                        QMessageBox.warning(self, "警告", 
                                          f"上证指数数据不足！请检查数据文件。")
                    else:
                        QMessageBox.warning(self, "警告", 
                                          f"{chinese_name}指数数据不足！请检查数据文件。")
                    continue
                
                # 选择需要的列
                result = merged[[
                    '日期',
                    '净空单变化量',
                    '指数变化量',
                    '净空单变化百分比',
                    '指数变化百分比'
                ]]
                
                # 保存结果，文件名包含滞后天数
                lag_str = f"_指数T+{lag_days}天" if lag_days > 0 else ""
                output_path = os.path.join(self.output_dir, f'{chinese_name}_数据{lag_str}.csv')
                result.to_csv(output_path, index=False, float_format='%.4f')
                self.stats_text.append(f"结果已保存到: {output_path}")
                
                # 进行统计分析
                stats = self.calculate_probabilities(result, contract_type, lag_days)
                all_stats.append((contract_type, stats, chinese_name))
            
            # 显示统计分析结果
            self.display_statistics(all_stats, lag_days)
            
            self.status_label.setText(f"处理完成！结果已保存到 {self.output_dir}")
            QMessageBox.information(self, "完成", f"数据处理完成！指数T+N天数={lag_days}")
        
        except Exception as e:
            self.status_label.setText("处理出错！")
            err_msg = f"处理过程中出错:\n{str(e)}\n\n{traceback.format_exc()}"
            self.stats_text.append(err_msg)
            QMessageBox.critical(self, "错误", err_msg)
    
    def run_comparison(self):
        """执行比较统计分析"""
        # 检查所有文件是否已选择
        if None in self.file_paths or self.output_dir is None:
            QMessageBox.warning(self, "警告", "请选择所有输入文件和输出目录！")
            return
        
        try:
            self.status_label.setText("正在执行比较统计分析...")
            self.stats_text.clear()  # 清空统计文本框
            self.stats_text.append("开始比较统计分析...")
            self.comparison_results = []
            self.delayed_results = []  # 清空之前的延迟结果
            self.significant_results = []  # 清空之前的显著变化结果
            self.significant_samples = []  # 清空之前的显著变化样本数据

            # 获取最大T+N天数 - 默认3天
            max_lag = self.max_lag_spin.value()
            
            # 获取显著变化阈值
            std_multiplier = self.std_threshold_spin.value()
            
            # 获取分位数阈值
            quantile_val = self.quantile_spin.value()
            
            # 获取显著变化阈值方法
            threshold_method_map = {
                "固定阈值": "fixed",
                "滚动阈值": "rolling",
                "分位数阈值": "quantile"
            }
            threshold_method = threshold_method_map.get(
                self.threshold_method_combo.currentText(), "fixed"
            )
            
            # 在统计文本中显示参数设置
            self.stats_text.append(f"\n参数设置:")
            self.stats_text.append(f"最大T+N天数: {max_lag}")
            self.stats_text.append(f"显著变化阈值: {std_multiplier}倍标准差")
            self.stats_text.append(f"分位数阈值: {quantile_val}%")
            self.stats_text.append(f"显著性阈值方法: {self.threshold_method_combo.currentText()}")
            
            # 处理每种指数类型
            contract_types = ['IC', 'IF', 'IH', 'IM', 'SH']  # 新增SH合约
            
            # 对于每个T+N天数（从1到最大滞后天数）
            for lag_days in range(1, max_lag + 1):
                for contract_type in contract_types:
                    chinese_name = self.get_chinese_name(contract_type)
                    # 更新状态
                    self.status_label.setText(f"处理 {chinese_name} 指数，T+{lag_days} 天...")
                    self.stats_text.append(f"\n=== 处理 {chinese_name} T+{lag_days} 天 ===")
                    QApplication.processEvents()
                    
                    # 处理数据
                    merged = self.process_contract_data(contract_type, lag_days)
                    
                    if merged.empty:
                        self.stats_text.append(f"警告: {chinese_name} T+{lag_days} 天无数据!")
                        continue
                    
                    # 进行统计分析
                    stats = self.calculate_probabilities(merged, contract_type, lag_days)
                    self.stats_text.append(f"统计结果:")
                    self.stats_text.append(f"  总样本数: {stats.get('总样本数', 'N/A')}")
                    self.stats_text.append(f"  净空单增加次数: {stats.get('净空单增加次数', 'N/A')}")
                    self.stats_text.append(f"  净空单减少次数: {stats.get('净空单减少次数', 'N/A')}")
                    
                    # 格式化概率为百分比形式
                    p1_value = stats.get('看跌概率', np.nan)
                    p1_str = f"{p1_value:.4f}" if not np.isnan(p1_value) else "N/A"
                    p2_value = stats.get('看涨概率', np.nan)
                    p2_str = f"{p2_value:.4f}" if not np.isnan(p2_value) else "N/A"
                    
                    # 添加百分比显示
                    p1_percent = f"({p1_value*100:.2f}%)" if not np.isnan(p1_value) else ""
                    p2_percent = f"({p2_value*100:.2f}%)" if not np.isnan(p2_value) else ""
                    
                    self.stats_text.append(f"  看跌概率(当净空单增加时指数减少的概率): {p1_str} {p1_percent}")
                    self.stats_text.append(f"  看涨概率(当净空单减少时指数增加的概率): {p2_str} {p2_percent}")
                    
                    # 显示平均涨幅
                    avg_up_pct = stats.get('净空单增加平均涨幅', np.nan)
                    avg_down_pct = stats.get('净空单减少平均涨幅', np.nan)
                    
                    avg_up_str = f"{avg_up_pct:.4f}%" if not np.isnan(avg_up_pct) else "N/A"
                    avg_down_str = f"{avg_down_pct:.4f}%" if not np.isnan(avg_down_pct) else "N/A"
                    
                    self.stats_text.append(f"  净空单增加时的平均涨幅: {avg_up_str}")
                    self.stats_text.append(f"  净空单减少时的平均涨幅: {avg_down_str}")
                    
                    # 存储结果
                    self.comparison_results.append({
                        '指数': chinese_name,  # 存储中文名称
                        '合约': contract_type,
                        '滞后天数': lag_days,
                        '看跌概率': stats.get('看跌概率', np.nan),
                        '看涨概率': stats.get('看涨概率', np.nan),
                        '净空单增加平均涨幅': stats.get('净空单增加平均涨幅', np.nan),
                        '净空单减少平均涨幅': stats.get('净空单减少平均涨幅', np.nan),
                        '总样本数': stats.get('总样本数', 0)
                    })
            
            # 计算补涨补跌概率（需要原始数据，不应用滞后）
            self.stats_text.append("\n=== 计算补涨补跌概率 ===")
            for contract_type in contract_types:
                chinese_name = self.get_chinese_name(contract_type)
                # 更新状态
                self.status_label.setText(f"计算 {chinese_name} 指数的补涨补跌概率...")
                self.stats_text.append(f"\n处理 {chinese_name} 指数...")
                QApplication.processEvents()
                
                # 获取原始数据（不应用滞后）
                merged = self.process_contract_data(contract_type, 0)
                
                if merged.empty:
                    self.stats_text.append(f"警告: {chinese_name} 指数无补涨补跌数据!")
                    continue
                
                # 计算延迟概率
                self.stats_text.append(f"计算延迟概率...")
                delayed_stats = self.calculate_delayed_probabilities(merged, contract_type, max_lag)
                self.delayed_results.extend(delayed_stats)
                
                # 显示延迟概率结果
                for stat in delayed_stats:
                    prob_str = f"{stat['概率']:.4f}" if not np.isnan(stat['概率']) else "N/A"
                    avg_pct_str = f"{stat['平均涨幅']:.4f}%" if not np.isnan(stat['平均涨幅']) else "N/A"
                    self.stats_text.append(
                        f"  {stat['类型']} T+{stat['延迟天数']}天: {prob_str} "
                        f"平均涨幅: {avg_pct_str} "
                        f"({stat['满足条件样本数']}/{stat['总样本数']})"
                    )
                
                # 计算显著变化影响
                self.stats_text.append(f"计算显著变化影响...")
                sig_stats, sig_samples = self.analyze_significant_change(
                    merged, contract_type, std_multiplier, max_lag, threshold_method, quantile_val
                )
                self.significant_results.extend(sig_stats)
                self.significant_samples.extend(sig_samples)  # 收集样本数据
                
                # 显示显著变化结果
                for stat in sig_stats:
                    # 获取平均变化量百分比
                    avg_change = stat.get('平均变化量百分比', np.nan)
                    avg_change_str = f"{avg_change:.4f}%" if not np.isnan(avg_change) else "N/A"
                    
                    # 获取概率值（如果存在）
                    prob_val = stat.get('概率', np.nan)
                    prob_str = f"{prob_val:.4f}" if not np.isnan(prob_val) else "N/A"

                    # 安全获取样本数信息
                    condition_count = stat.get('满足条件样本数', 'N/A')
                    total_count = stat.get('总样本数', 'N/A')
                    
    
                    self.stats_text.append(
                        f"  {stat['变化类型']} T+{stat['滞后天数']}天: "
                        f"概率: {prob_str} 平均变化量: {avg_change_str} "
                        f"({condition_count}/{total_count})"
                    )
            
            # 显示比较结果
            self.display_comparison()
            
            # 绘制图表
            self.plot_comparison()
            
            # 生成结论报告
            conclusion_report = self.generate_conclusion_report(
                self.comparison_results, 
                self.delayed_results,
                self.significant_results
            )
            
            # 在界面上显示报告
            self.stats_text.append("\n=== 分析结论 ===")
            self.stats_text.append(conclusion_report)
            self.tab_widget.setCurrentIndex(0)  # 切换到统计标签页
            
            # 保存报告到文件
            max_lag_val = self.max_lag_spin.value()
            report_path = os.path.join(self.output_dir, f'分析结论_最大T+{max_lag_val}天.txt')
            with open(report_path, 'w', encoding='utf-8') as f:
                f.write(conclusion_report)
            
            self.status_label.setText(f"比较统计分析完成！结论报告已保存到 {report_path}")
            QMessageBox.information(self, "完成", "比较统计分析完成！")
        
        except Exception as e:
            self.status_label.setText("比较统计出错！")
            err_msg = f"比较统计分析过程中出错:\n{str(e)}\n\n{traceback.format_exc()}"
            self.stats_text.append(err_msg)
            QMessageBox.critical(self, "详细错误", err_msg)
    
    def display_statistics(self, all_stats, lag_days):
        """在文本框中显示统计分析结果"""
        if not all_stats:
            self.stats_text.append("无统计结果")
            return
            
        # 创建标题
        self.stats_text.append(f"统计分析结果 (指数T+N天数={lag_days})")
        self.stats_text.append("=" * 80)
        
        # 显示每个指数的统计结果
        for contract_type, stats, chinese_name in all_stats:
            self.stats_text.append(f"\n指数: {chinese_name}")
            self.stats_text.append("-" * 60)
            
            # 基本信息
            self.stats_text.append(f"总样本数: {stats['总样本数']}")
            self.stats_text.append(f"净空单增加次数: {stats['净空单增加次数']} ({stats['净空单增加次数']/stats['总样本数']:.2%})")
            self.stats_text.append(f"净空单减少次数: {stats['净空单减少次数']} ({stats['净空单减少次数']/stats['总样本数']:.2%})")
            self.stats_text.append(f"指数增加次数: {stats['指数增加次数']} ({stats['指数增加次数']/stats['总样本数']:.2%})")
            self.stats_text.append(f"指数减少次数: {stats['指数减少次数']} ({stats['指数减少次数']/stats['总样本数']:.2%})")
            
            # 看跌和看涨概率
            self.stats_text.append("\n条件概率:")
            self.stats_text.append(f"看跌概率(当净空单增加时指数减少的概率): {stats['看跌概率']:.4f} ({stats['看跌概率']:.2%})")
            self.stats_text.append(f"看涨概率(当净空单减少时指数增加的概率): {stats['看涨概率']:.4f} ({stats['看涨概率']:.2%})")
            
            # 平均涨幅
            self.stats_text.append("\n平均涨幅:")
            self.stats_text.append(f"净空单增加时的平均涨幅: {stats['净空单增加平均涨幅']:.4f}%")
            self.stats_text.append(f"净空单减少时的平均涨幅: {stats['净空单减少平均涨幅']:.4f}%")
            
            self.stats_text.append("=" * 80)
    
    def display_comparison(self):
        """在表格中显示比较统计结果"""
        # 按指数比较表格
        if self.comparison_results:
            index_names = sorted(set(r['指数'] for r in self.comparison_results))
            lag_days_list = sorted(set(r['滞后天数'] for r in self.comparison_results))
            
            # 按指数比较表格
            self.contract_comparison_table.setRowCount(len(lag_days_list))
            # 增加列数以容纳平均涨幅数据
            self.contract_comparison_table.setColumnCount(len(index_names) * 4)  # 每个指数4列：看跌概率、看跌平均涨幅、看涨概率、看涨平均涨幅
            
            # 设置表头
            headers = []
            for index_name in index_names:
                headers.append(f"{index_name} 看跌")
                headers.append(f"{index_name} 看跌平均涨幅")
                headers.append(f"{index_name} 看涨")
                headers.append(f"{index_name} 看涨平均涨幅")
            self.contract_comparison_table.setHorizontalHeaderLabels(headers)
            
            # 设置行头（滞后天数）
            self.contract_comparison_table.setVerticalHeaderLabels([f"T+{d}天" for d in lag_days_list])
            
            # 填充数据
            for lag_idx, lag_days in enumerate(lag_days_list):
                for index_idx, index_name in enumerate(index_names):
                    # 查找对应的结果
                    result = next((r for r in self.comparison_results 
                                  if r['滞后天数'] == lag_days and r['指数'] == index_name), None)
                    
                    if result:
                        col_offset = index_idx * 4
                        
                        # 看跌列
                        p1_item = QTableWidgetItem(f"{result['看跌概率']:.4f}")
                        p1_item.setTextAlignment(Qt.AlignCenter)
                        self.contract_comparison_table.setItem(lag_idx, col_offset, p1_item)
                        
                        # 看跌平均涨幅列
                        p1_avg_item = QTableWidgetItem(f"{result['净空单增加平均涨幅']:.4f}%")
                        p1_avg_item.setTextAlignment(Qt.AlignCenter)
                        self.contract_comparison_table.setItem(lag_idx, col_offset + 1, p1_avg_item)
                        
                        # 看涨列
                        p2_item = QTableWidgetItem(f"{result['看涨概率']:.4f}")
                        p2_item.setTextAlignment(Qt.AlignCenter)
                        self.contract_comparison_table.setItem(lag_idx, col_offset + 2, p2_item)
                        
                        # 看涨平均涨幅列
                        p2_avg_item = QTableWidgetItem(f"{result['净空单减少平均涨幅']:.4f}%")
                        p2_avg_item.setTextAlignment(Qt.AlignCenter)
                        self.contract_comparison_table.setItem(lag_idx, col_offset + 3, p2_avg_item)
        
        # 显示补涨补跌概率
        if self.delayed_results:
            # 获取最大滞后天数（从参数设置中获取）
            max_lag_val = self.max_lag_spin.value()  # 修复：使用 max_lag_val 替代 max_lag
            
            # 按指数和类型分组
            contract_types = sorted(set(r['合约'] for r in self.delayed_results))
            max_delay = max(r['延迟天数'] for r in self.delayed_results) if self.delayed_results else 0
            
            # 创建表格
            self.delayed_table.setRowCount(len(contract_types) * 2)  # 每个指数两行（补涨和补跌）
            # 增加列数以容纳平均涨幅数据
            self.delayed_table.setColumnCount(max_lag_val + 1)  # 延迟天数 + 类型列 + 平均涨幅列
            
            # 设置表头
            headers = ["指数/类型"] 
            for d in range(2, max_lag_val + 1):  # 从T+2天开始
                headers.append(f"T+{d}天概率")
                headers.append(f"T+{d}天平均涨幅")
            self.delayed_table.setHorizontalHeaderLabels(headers)
            
            # 填充数据
            for row_idx, contract in enumerate(contract_types):
                chinese_name = self.get_chinese_name(contract)
                
                # 补跌行
                drop_row = row_idx * 2
                drop_item = QTableWidgetItem(f"{chinese_name} (补跌)")
                drop_item.setTextAlignment(Qt.AlignCenter)
                self.delayed_table.setItem(drop_row, 0, drop_item)
                
                # 补涨行
                rise_row = row_idx * 2 + 1
                rise_item = QTableWidgetItem(f"{chinese_name} (补涨)")
                rise_item.setTextAlignment(Qt.AlignCenter)
                self.delayed_table.setItem(rise_row, 0, rise_item)
                
                # 填充概率值
                for delay in range(2, max_lag_val + 1):  # 修复：使用 max_lag_val 替代 max_lag
                    col_idx = (delay - 2) * 2 + 1  # 列索引从1开始，每列包含概率和平均涨幅
                    
                    # 查找补跌概率
                    drop_prob = next(
                        (r for r in self.delayed_results 
                        if r['合约'] == contract and r['类型'] == '补跌' and r['延迟天数'] == delay),
                        None
                    )
                    
                    # 查找补涨概率
                    rise_prob = next(
                        (r for r in self.delayed_results 
                        if r['合约'] == contract and r['类型'] == '补涨' and r['延迟天数'] == delay),
                        None
                    )
                    
                    # 设置补跌概率
                    if drop_prob:
                        # 概率值
                        prob_item = QTableWidgetItem(f"{drop_prob['概率']:.4f}")
                        prob_item.setTextAlignment(Qt.AlignCenter)
                        self.delayed_table.setItem(drop_row, col_idx, prob_item)
                        
                        # 平均涨幅
                        avg_item = QTableWidgetItem(f"{drop_prob['平均涨幅']:.4f}%")
                        avg_item.setTextAlignment(Qt.AlignCenter)
                        self.delayed_table.setItem(drop_row, col_idx + 1, avg_item)
                    
                    # 设置补涨概率
                    if rise_prob:
                        # 概率值
                        prob_item = QTableWidgetItem(f"{rise_prob['概率']:.4f}")
                        prob_item.setTextAlignment(Qt.AlignCenter)
                        self.delayed_table.setItem(rise_row, col_idx, prob_item)
                        
                        # 平均涨幅
                        avg_item = QTableWidgetItem(f"{rise_prob['平均涨幅']:.4f}%")
                        avg_item.setTextAlignment(Qt.AlignCenter)
                        self.delayed_table.setItem(rise_row, col_idx + 1, avg_item)
        
        # 显示显著变化分析结果
        if self.significant_results:
            # 创建表格
            self.significant_table.setRowCount(len(self.significant_results))
            # 增加列数以容纳新增的参数信息和平均涨幅
            self.significant_table.setColumnCount(15)
            
            # 设置表头 - 增加平均涨幅列
            headers = [
                "指数", "变化类型", "T+N天数", "概率类型", "概率值", 
                "平均变化量百分比",  # 新增的列
                "满足条件样本数", "总样本数", "方法", "标准差倍数", "分位数(%)", 
                "净空单均值", "净空单标准差", "阈值"
            ]
            self.significant_table.setHorizontalHeaderLabels(headers)
            
            # 安全格式化函数
            def safe_format(value, fmt='.2f'):
                """安全格式化数值，处理字符串和NaN情况"""
                try:
                    # 尝试转换为浮点数
                    num = float(value)
                    # 检查是否为NaN
                    if np.isnan(num):
                        return "N/A"
                    return format(num, fmt)
                except (TypeError, ValueError):
                    # 如果转换失败，直接返回原值
                    return str(value)
            
            # 填充数据
            for row_idx, result in enumerate(self.significant_results):
                # 指数（使用中文名称）
                chinese_name = self.get_chinese_name(result['合约'])
                contract_item = QTableWidgetItem(chinese_name)
                contract_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 0, contract_item)
                
                # 变化类型
                change_type_item = QTableWidgetItem(result['变化类型'])
                change_type_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 1, change_type_item)
                
                # T+N天数
                lag_item = QTableWidgetItem(f"T+{result['滞后天数']}")
                lag_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 2, lag_item)
                
                # 概率类型和值
                prob_type_item = QTableWidgetItem(f"{result['变化描述']}概率")
                prob_type_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 3, prob_type_item)
                
                prob_value_val = result['概率'] if '概率' in result else np.nan
                prob_value_str = safe_format(prob_value_val, '.4f')
                prob_value_item = QTableWidgetItem(prob_value_str)
                prob_value_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 4, prob_value_item)
                
                # 平均变化量百分比 - 新增
                avg_change_val = result.get('平均变化量百分比', np.nan)
                avg_change_str = safe_format(avg_change_val, '.4f') + "%" if not np.isnan(avg_change_val) else "N/A"
                avg_change_item = QTableWidgetItem(avg_change_str)
                avg_change_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 5, avg_change_item)
                
                # 满足条件样本数
                count_item = QTableWidgetItem(str(result.get('满足条件样本数', 'N/A')))
                count_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 6, count_item)
                
                # 总样本数
                total_item = QTableWidgetItem(str(result.get('总样本数', 'N/A')))
                total_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 7, total_item)
                
                # 方法
                method = result.get('方法', '固定阈值法')
                method_item = QTableWidgetItem(method)
                method_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 8, method_item)
                
                # 标准差倍数
                std_multiplier_val = result.get('标准差倍数', 'N/A')
                std_multiplier_str = safe_format(std_multiplier_val, '.2f')
                std_multiplier_item = QTableWidgetItem(std_multiplier_str)
                std_multiplier_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 9, std_multiplier_item)
                
                # 分位数
                quantile_val = result.get('分位数', 'N/A')
                quantile_str = safe_format(quantile_val, '.1f')
                quantile_item = QTableWidgetItem(quantile_str)
                quantile_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 10, quantile_item)
                
                # 净空单均值
                net_short_mean_val = result.get('净空单均值', 'N/A')
                net_short_mean_str = safe_format(net_short_mean_val, '.2f')
                net_short_mean_item = QTableWidgetItem(net_short_mean_str)
                net_short_mean_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 11, net_short_mean_item)
                
                # 净空单标准差
                net_short_std_val = result.get('净空单标准差', 'N/A')
                net_short_std_str = safe_format(net_short_std_val, '.2f')
                net_short_std_item = QTableWidgetItem(net_short_std_str)
                net_short_std_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 12, net_short_std_item)
                
                # 阈值
                threshold_val = result.get('阈值', 'N/A')
                threshold_str = safe_format(threshold_val, '.2f')
                threshold_item = QTableWidgetItem(threshold_str)
                threshold_item.setTextAlignment(Qt.AlignCenter)
                self.significant_table.setItem(row_idx, 13, threshold_item)
    
    def plot_comparison(self):
        """绘制比较统计图表"""
        if not self.comparison_results:
            return
            
        # 创建新的图表对象
        figure = plt.figure(figsize=(12, 9), dpi=100)
        
        # 设置支持中文的字体
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Zen Hei']
        plt.rcParams['axes.unicode_minus'] = False
        
        # 按指数分组数据
        index_data = {}
        for result in self.comparison_results:
            index_name = result['指数']  # 使用中文名称
            if index_name not in index_data:
                index_data[index_name] = {'lags': [], 'p1': [], 'p2': [], 'avg_up': [], 'avg_down': []}
            
            index_data[index_name]['lags'].append(result['滞后天数'])
            index_data[index_name]['p1'].append(result['看跌概率'])
            index_data[index_name]['p2'].append(result['看涨概率'])
            index_data[index_name]['avg_up'].append(result['净空单增加平均涨幅'])
            index_data[index_name]['avg_down'].append(result['净空单减少平均涨幅'])
        
        # 创建图表
        ax1 = figure.add_subplot(211)
        ax2 = figure.add_subplot(212)
        
        # 计算纵轴范围
        all_p1 = [p for data in index_data.values() for p in data['p1'] if not np.isnan(p)]
        all_p2 = [p for data in index_data.values() for p in data['p2'] if not np.isnan(p)]
        
        # 确保有数据
        if not all_p1 or not all_p2:
            return
            
        # 计算纵轴范围，确保最小值和最大值之间有足够的间隔
        p1_min = max(0, min(all_p1) - 0.05)
        p1_max = min(1, max(all_p1) + 0.05)
        p2_min = max(0, min(all_p2) - 0.05)
        p2_max = min(1, max(all_p2) + 0.05)
        
        # 确保纵轴范围有最小间隔，避免数据过于集中时看不清变化
        if p1_max - p1_min < 0.1:
            mid = (p1_min + p1_max) / 2
            p1_min = max(0, mid - 0.15)
            p1_max = min(1, mid + 0.15)
            
        if p2_max - p2_min < 0.1:
            mid = (p2_min + p2_max) / 2
            p2_min = max(0, mid - 0.15)
            p2_max = min(1, mid + 0.15)
        
        # 绘制看跌概率随滞后天数变化的曲线
        for index_name, data in index_data.items():
            ax1.plot(data['lags'], data['p1'], 'o-', markersize=6, label=f"{index_name} 看跌")
            
        ax1.set_title('看跌概率(当净空单增加时指数减少)随T+N天数的变化')
        ax1.set_xlabel('T+N天数')
        ax1.set_ylabel('概率')
        ax1.legend()
        ax1.grid(True, linestyle='--', alpha=0.7)
        ax1.set_ylim(p1_min, p1_max)  # 动态设置纵轴范围
        ax1.axhline(y=0.5, color='gray', linestyle='--', alpha=0.5)  # 添加参考线
        
        # 设置x轴刻度为整数
        ax1.set_xticks(list(set(lag for data in index_data.values() for lag in data['lags'])))
        
        # 在数据点上添加数值标签
        for index_name, data in index_data.items():
            for x, y in zip(data['lags'], data['p1']):
                if not np.isnan(y):
                    ax1.text(x, y, f'{y:.3f}', fontsize=8, ha='center', va='bottom')
        
        # 绘制看涨概率随滞后天数变化的曲线
        for index_name, data in index_data.items():
            ax2.plot(data['lags'], data['p2'], 's--', markersize=6, label=f"{index_name} 看涨")
            
        ax2.set_title('看涨概率(当净空单减少时指数增加)随T+N天数的变化')
        ax2.set_xlabel('T+N天数')
        ax2.set_ylabel('概率')
        ax2.legend()
        ax2.grid(True, linestyle='--', alpha=0.7)
        ax2.set_ylim(p2_min, p2_max)  # 动态设置纵轴范围
        ax2.axhline(y=0.5, color='gray', linestyle='--', alpha=0.5)  # 添加参考线
        
        # 设置x轴刻度为整数
        ax2.set_xticks(list(set(lag for data in index_data.values() for lag in data['lags'])))
        
        # 在数据点上添加数值标签
        for index_name, data in index_data.items():
            for x, y in zip(data['lags'], data['p2']):
                if not np.isnan(y):
                    ax2.text(x, y, f'{y:.3f}', fontsize=8, ha='center', va='bottom')
        
        # 添加总标题
        figure.suptitle('净空单与指数关系分析', fontsize=14)
        
        # 调整布局
        figure.tight_layout()
        figure.subplots_adjust(top=0.9)  # 为总标题留出空间
        
        # 保存图表对象
        self.last_comparison_figure = figure
        
        # 在状态栏显示提示
        self.status_label.setText("比较统计图表已准备好，请点击'查看比较统计图表'按钮查看")
    
    def export_statistics(self, lag_days):
        """导出统计结果为CSV文件"""
        # 重新计算统计结果
        all_stats = []
        contract_types = ['IC', 'IF', 'IH', 'IM', 'SH']  # 新增SH合约
        
        for contract_type in contract_types:
            # 处理数据
            merged = self.process_contract_data(contract_type, lag_days)
            
            if merged.empty:
                continue
            
            # 进行统计分析
            stats = self.calculate_probabilities(merged, contract_type, lag_days)
            all_stats.append((contract_type, stats, self.get_chinese_name(contract_type)))
        
        if not all_stats or not self.output_dir:
            return
            
        # 创建数据框
        stats_list = []
        for contract_type, stats, chinese_name in all_stats:
            row = {
                '指数': chinese_name,
                '合约': contract_type,
                '滞后天数': lag_days,
                '总样本数': stats['总样本数'],
                '净空单增加次数': stats['净空单增加次数'],
                '净空单减少次数': stats['净空单减少次数'],
                '指数增加次数': stats['指数增加次数'],
                '指数减少次数': stats['指数减少次数'],
                '看跌概率(当净空单增加时指数减少)': stats['看跌概率'],
                '看涨概率(当净空单减少时指数增加)': stats['看涨概率'],
                '净空单增加平均涨幅(%)': stats.get('净空单增加平均涨幅', np.nan),
                '净空单减少平均涨幅(%)': stats.get('净空单减少平均涨幅', np.nan)
            }
            stats_list.append(row)
        
        stats_df = pd.DataFrame(stats_list)
        
        # 保存文件
        lag_str = f"_指数T+{lag_days}天" if lag_days > 0 else ""
        output_path = os.path.join(self.output_dir, f'统计结果{lag_str}.csv')
        stats_df.to_csv(output_path, index=False, float_format='%.4f')
        
        QMessageBox.information(self, "导出成功", f"统计结果已导出到: {output_path}")
    
    def export_comparison(self):
        """导出比较结果为CSV文件"""
        # 导出比较统计结果
        if self.comparison_results:
            # 创建数据框
            stats_df = pd.DataFrame(self.comparison_results)
            
            # 重命名列
            stats_df.rename(columns={
                '看跌概率': '看跌概率',
                '看涨概率': '看涨概率',
                '净空单增加平均涨幅': '净空单增加平均涨幅(%)',
                '净空单减少平均涨幅': '净空单减少平均涨幅(%)'
            }, inplace=True)
            
            # 保存文件
            max_lag = self.max_lag_spin.value()
            output_path = os.path.join(self.output_dir, f'比较统计结果_最大T+{max_lag}天.csv')
            stats_df.to_csv(output_path, index=False, float_format='%.4f')
        
        # 导出补涨补跌结果
        if self.delayed_results:
            delayed_df = pd.DataFrame(self.delayed_results)
            max_lag = self.max_lag_spin.value()
            delayed_path = os.path.join(self.output_dir, f'补涨补跌概率_最大T+{max_lag}天.csv')
            delayed_df.to_csv(delayed_path, index=False, float_format='%.4f')
        
        # 导出显著变化结果 - 包含所有参数信息和平均涨幅
        if self.significant_results:
            sig_df = pd.DataFrame(self.significant_results)
            max_lag = self.max_lag_spin.value()
            sig_path = os.path.join(self.output_dir, f'净空单显著变化影响_平均涨幅_最大T+{max_lag}天.csv')
            
            # 选择需要的列并重命名
            columns_to_export = [
                '合约', '变化类型', '滞后天数', '概率', '平均变化量百分比', 
                '满足条件样本数', '总样本数', '变化描述', '方法', '标准差倍数', '分位数',
                '净空单均值', '净空单标准差', '阈值'
            ]
            
            # 添加中文名称列
            sig_df['指数'] = sig_df['合约'].apply(self.get_chinese_name)
            
            # 重新排序列
            sig_df = sig_df[['指数'] + columns_to_export]
            
            # 重命名列
            column_rename_map = {
                '合约': '合约代码',
                '变化类型': '变化类型',
                '滞后天数': '滞后天数',
                '概率': '概率',
                '平均变化量百分比': '平均变化量百分比(%)',
                '满足条件样本数': '满足条件样本数',
                '总样本数': '总样本数',
                '变化描述': '变化描述',
                '方法': '计算方法',
                '标准差倍数': '标准差倍数',
                '分位数': '分位数(%)',
                '净空单均值': '净空单均值',
                '净空单标准差': '净空单标准差',
                '阈值': '阈值'
            }
            
            sig_df.rename(columns=column_rename_map, inplace=True)
            
            # 保存文件
            sig_df.to_csv(sig_path, index=False, float_format='%.4f')
            
            # 在状态栏显示导出信息
            self.status_label.setText(f"显著变化结果已导出，包含{len(sig_df)}条记录")
        
        QMessageBox.information(self, "导出成功", "比较统计结果已导出到输出目录")

    def export_significant_samples(self):
        """导出显著变化样本数据"""
        if not self.significant_samples:
            QMessageBox.warning(self, "警告", "没有可导出的显著变化样本数据！")
            return
        
        try:
            if not self.output_dir:
                QMessageBox.warning(self, "警告", "请先选择输出目录！")
                return

            
            max_lag_val = self.max_lag_spin.value()
            output_dir = os.path.join(self.output_dir, f"显著变化样本数据_最大T+{max_lag_val}天")
            os.makedirs(output_dir, exist_ok=True)
            
            # 合并所有样本数据
            if self.significant_samples:
                all_samples = pd.concat(self.significant_samples, ignore_index=True)
            else:
                QMessageBox.warning(self, "警告", "没有可导出的显著变化样本数据！")
                return
            
            # 按合约、方法和变化类型分组
            grouped = all_samples.groupby(['合约', '方法', '变化类型'])
            file_count = 0
            
            for (contract, method, change_type), group in grouped:
                # 获取中文名称
                chinese_name = self.get_chinese_name(contract)
                
                # 方法名称映射
                method_name_map = {
                    '固定阈值法': '固定阈值',
                    '滚动阈值法': '滚动阈值',
                    '分位数阈值法': '分位数阈值'
                }
                method_name = method_name_map.get(method, method)
                
                # 变化类型映射
                change_type_map = {
                    '固定阈值_显著增大': '显著增大',
                    '固定阈值_显著减小': '显著减小',
                    '滚动阈值_显著增大': '显著增大',
                    '滚动阈值_显著减小': '显著减小',
                    '分位数阈值_显著增大': '显著增大',
                    '分位数阈值_显著减小': '显著减小'
                }
                change_type_name = change_type_map.get(change_type, change_type)
                
                # 选择需要的列
                columns_to_keep = [
                    '日期', '净空单变化量', '指数变化量', '净空单变化百分比', '指数变化百分比',
                    '滞后天数', '阈值', '是否下跌', '是否上涨'
                ]
                
                # 添加延迟列
                for k in range(1, max_lag_val + 1):
                    col_name = f'指数变化量_延迟{k}天'
                    pct_col_name = f'指数变化百分比_延迟{k}天'
                    if col_name in group.columns:
                        columns_to_keep.append(col_name)
                        columns_to_keep.append(pct_col_name)
                
                # 保留需要的列
                group = group[columns_to_keep]
                
                # 重命名列
                column_rename = {
                    '日期': '日期',
                    '净空单变化量': '净空单变化量',
                    '指数变化量': '指数变化量(T日)',
                    '净空单变化百分比': '净空单变化百分比',
                    '指数变化百分比': '指数变化百分比(T日)',
                    '滞后天数': '滞后天数',
                    '阈值': '显著变化阈值',
                    '是否下跌': '是否下跌',
                    '是否上涨': '是否上涨'
                }
                
                # 添加延迟列的说明
                for k in range(1, max_lag_val + 1):
                    col_name = f'指数变化量_延迟{k}天'
                    pct_col_name = f'指数变化百分比_延迟{k}天'
                    if col_name in group.columns:
                        column_rename[col_name] = f'指数变化量(T+{k}日)'
                        column_rename[pct_col_name] = f'指数变化百分比(T+{k}日)'
                
                group.rename(columns=column_rename, inplace=True)
                
                # 保存文件
                filename = f"显著变化样本_{chinese_name}_{method_name}_{change_type_name}_最大T+{max_lag_val}天.csv"
                file_path = os.path.join(output_dir, filename)
                group.to_csv(file_path, index=False, float_format='%.4f')
                file_count += 1
                self.stats_text.append(f"已保存: {filename}")            
                
            QMessageBox.information(self, "导出成功", f"已保存 {file_count} 个显著变化样本数据文件到:\n{output_dir}")
            self.status_label.setText(f"导出成功: {file_count}个文件")
        
        except Exception as e:
            QMessageBox.critical(self, "导出失败", f"导出样本数据时出错:\n{str(e)}")
            self.status_label.setText("导出样本数据失败")

    def generate_conclusion_report(self, results, delayed_results, sig_results):
        """根据统计结果生成详细结论报告"""
        # 实现代码较长，此处省略
        return "分析结论报告内容"
        
    def toggle_quantile_range(self):
        """切换分位数范围控件的可用状态"""
        method = self.curve_method_combo.currentData()
        chart_type = self.chart_type_combo.currentText()  # 获取图表类型选择
        is_quantile = method == "quantile"
        
        self.quantile_label.setEnabled(is_quantile)
        self.quantile_start_spin.setEnabled(is_quantile)
        self.quantile_end_spin.setEnabled(is_quantile)

    def plot_significance_curve(self):
        """绘制显著性变化曲线（一行两列布局：左侧显著增大，右侧显著减小）"""
        try:
            # 获取参数
            contract_type = self.curve_contract_combo.currentData()
            chinese_name = self.get_chinese_name(contract_type)
            method = self.curve_method_combo.currentData()
            start = self.start_spin.value()
            end = self.end_spin.value()
            step = self.step_spin.value()
            quantile_start = self.quantile_start_spin.value()
            quantile_end = self.quantile_end_spin.value()
            chart_type = self.chart_type_combo.currentText()  # 获取图表类型选择
            
            # 获取最大滞后天数
            max_lag = self.max_lag_spin.value()
            
            # 验证参数
            if start >= end:
                QMessageBox.warning(self, "参数错误", "阈值范围的起始值必须小于结束值")
                return
                
            if step <= 0:
                QMessageBox.warning(self, "参数错误", "步长必须大于0")
                return
                
            # 更新状态
            self.status_label.setText(f"正在计算 {chinese_name} 显著性变化曲线...")
            QApplication.processEvents()
            
            # 获取原始数据（不应用滞后）
            merged = self.process_contract_data(contract_type, 0)
            
            if merged.empty:
                QMessageBox.warning(self, "数据错误", f"{chinese_name} 无可用数据！")
                return
                
            # 生成阈值范围
            if method == "quantile":
                # 分位数阈值范围
                thresholds = np.arange(quantile_start, quantile_end + 0.1, step)
                x_label = "分位数阈值(%)"
            else:
                # 标准差倍数范围
                thresholds = np.arange(start, end + 0.01, step)
                x_label = "标准差倍数"
            
            # 存储结果 - 按变化类型和滞后天数分组
            results = {
                'increase': {},  # 显著增大
                'decrease': {}   # 显著减小
            }
            
            # 初始化所有滞后天数的结果列表
            for lag in range(1, max_lag + 1):
                results['increase'][lag] = []
                results['decrease'][lag] = []
            
            for threshold in thresholds:
                # 分析显著变化
                if method == "quantile":
                    sig_stats, _ = self.analyze_significant_change(
                        merged, contract_type, 
                        std_multiplier=1.0,  # 不使用标准差倍数
                        max_lag=max_lag, 
                        threshold_method=method, 
                        quantile=threshold
                    )
                else:
                    sig_stats, _ = self.analyze_significant_change(
                        merged, contract_type, 
                        std_multiplier=threshold, 
                        max_lag=max_lag, 
                        threshold_method=method, 
                        quantile=90.0  # 不使用分位数
                    )
                
                # 按变化类型和滞后天数整理结果
                for stat in sig_stats:
                    if stat.get('方法', '') == {
                        'fixed': '固定阈值法',
                        'rolling': '滚动阈值法',
                        'quantile': '分位数阈值法'
                    }[method]:
                        change_type = 'increase' if stat['变化类型'] == '显著增大' else 'decrease'
                        lag_days = stat['滞后天数']
                        
                        # 添加到结果列表
                        results[change_type][lag_days].append({
                            '阈值': threshold,
                            '概率': stat.get('概率', np.nan),
                            '平均变化量百分比': stat.get('平均变化量百分比', np.nan),
                            '满足条件样本数': stat.get('满足条件样本数', 0),
                            '总样本数': stat.get('总样本数', 0)
                        })
            
            # 创建新的图表对象 - 一行两列布局
            figure = plt.figure(figsize=(16, 8), dpi=100)  # 调整尺寸以适应一行两列
            
            # 设置支持中文的字体 
            plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Zen Hei']
            plt.rcParams['axes.unicode_minus'] = False
            
            # 左侧图表：显著增大（看跌）
            ax_increase = figure.add_subplot(121)
            # 右侧图表：显著减小（看涨）
            ax_decrease = figure.add_subplot(122)
            
            # 设置颜色、线型和标记
            colors = plt.cm.tab10(np.linspace(0, 1, max_lag))
            markers = ['o', 's', '^', 'D', 'v', '<', '>', 'p', '*', 'h']
            line_width = 2.0
            
            # 1. 绘制左侧图表：显著增大（看跌）
            for lag in range(1, max_lag + 1):
                lag_results = results['increase'].get(lag, [])
                if not lag_results:
                    continue
                    
                # 转换为数据框
                df = pd.DataFrame(lag_results)
                df = df.sort_values('阈值')
                
                # 根据图表类型绘制曲线
                if chart_type == "概率图" or chart_type == "两者都显示":
                    # 绘制概率曲线
                    if not df['概率'].isnull().all():
                        ax_increase.plot(
                            df['阈值'], df['概率'], 
                            color=colors[lag-1],
                            marker=markers[lag-1], markersize=8, 
                            label=f'T+{lag}天', linewidth=line_width
                        )
                        
                        # 添加数据点标签
                        for idx, row in df.iterrows():
                            prob_value = row.get('概率', np.nan)
                            if not np.isnan(prob_value):
                                ax_increase.text(
                                    row['阈值'], prob_value + 0.01,
                                    f"{prob_value:.3f}", 
                                    fontsize=9, ha='center', va='bottom'
                                )
                
                if chart_type == "平均变化量百分比图" or chart_type == "两者都显示":
                    # 绘制平均变化量百分比曲线
                    if not df['平均变化量百分比'].isnull().all():
                        ax_increase.plot(
                            df['阈值'], df['平均变化量百分比'], 
                            color=colors[lag-1],
                            marker=markers[lag-1], markersize=8, 
                            linestyle='--' if chart_type == "两者都显示" else '-',  # 当显示两条线时使用虚线区分
                            label=f'T+{lag}天 (平均变化量)' if chart_type == "两者都显示" else f'T+{lag}天',
                            linewidth=line_width
                        )
                        
                        # 添加数据点标签
                        for _, row in df.iterrows():
                            if not np.isnan(row['平均变化量百分比']):
                                # 使用红色表示负值，绿色表示正值
                                color = 'red' if row['平均变化量百分比'] < 0 else 'green'
                                ax_increase.text(
                                    row['阈值'], row['平均变化量百分比'] + 0.1,
                                    f"{row['平均变化量百分比']:.2f}%", 
                                    fontsize=9, ha='center', va='bottom', color=color
                                )
            
            # 设置左侧图表属性
            ax_increase.set_title(f"{chinese_name} 净空单显著增大", fontsize=14)
            ax_increase.set_xlabel(x_label, fontsize=12)
            
            if chart_type == "概率图":
                ax_increase.set_ylabel('下跌概率', fontsize=12)
                ax_increase.axhline(y=0.5, color='r', linestyle='--', alpha=0.7)
                if not ax_increase.lines:  # 如果没有线条，设置默认范围
                    ax_increase.set_ylim(0, 1.0)
                else:
                    ax_increase.set_ylim(0, 1.0)  # 固定概率范围
            elif chart_type == "平均变化量百分比图":
                ax_increase.set_ylabel('平均变化量百分比(%)', fontsize=12)
                ax_increase.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
            else:  # 两者都显示
                # 设置双Y轴标签
                ax_increase.set_ylabel('下跌概率', fontsize=12, color='blue')
                ax_increase.tick_params(axis='y', labelcolor='blue')
                
                # 创建第二个Y轴用于平均变化量百分比
                ax_increase2 = ax_increase.twinx()
                ax_increase2.set_ylabel('平均变化量百分比(%)', fontsize=12, color='green')
                ax_increase2.tick_params(axis='y', labelcolor='green')
                
                # 添加参考线
                ax_increase.axhline(y=0.5, color='r', linestyle='--', alpha=0.7)
                ax_increase2.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
            
            ax_increase.grid(True, linestyle='--', alpha=0.6)
            if ax_increase.lines:  # 只在有线条时添加图例
                ax_increase.legend(loc='best', fontsize=10)
            
            # 2. 绘制右侧图表：显著减小（看涨）
            for lag in range(1, max_lag + 1):
                lag_results = results['decrease'].get(lag, [])
                if not lag_results:
                    continue
                    
                # 转换为数据框
                df = pd.DataFrame(lag_results)
                df = df.sort_values('阈值')
                
                # 根据图表类型绘制曲线
                if chart_type == "概率图" or chart_type == "两者都显示":
                    # 绘制概率曲线
                    if not df['概率'].isnull().all():
                        ax_decrease.plot(
                            df['阈值'], df['概率'], 
                            color=colors[lag-1],
                            marker=markers[lag-1], markersize=8, 
                            label=f'T+{lag}天', linewidth=line_width
                        )
                        
                        # 添加数据点标签
                        for idx, row in df.iterrows():
                            prob_value = row.get('概率', np.nan)
                            if not np.isnan(prob_value):
                                ax_decrease.text(
                                    row['阈值'], prob_value + 0.01,
                                    f"{prob_value:.3f}", 
                                    fontsize=9, ha='center', va='bottom'
                                )
                
                if chart_type == "平均变化量百分比图" or chart_type == "两者都显示":
                    # 绘制平均变化量百分比曲线
                    if not df['平均变化量百分比'].isnull().all():
                        ax_decrease.plot(
                            df['阈值'], df['平均变化量百分比'], 
                            color=colors[lag-1],
                            marker=markers[lag-1], markersize=8, 
                            linestyle='--' if chart_type == "两者都显示" else '-',  # 当显示两条线时使用虚线区分
                            label=f'T+{lag}天 (平均变化量)' if chart_type == "两者都显示" else f'T+{lag}天',
                            linewidth=line_width
                        )
                        
                        # 添加数据点标签
                        for _, row in df.iterrows():
                            if not np.isnan(row['平均变化量百分比']):
                                # 使用红色表示负值，绿色表示正值
                                color = 'red' if row['平均变化量百分比'] < 0 else 'green'
                                ax_decrease.text(
                                    row['阈值'], row['平均变化量百分比'] + 0.1,
                                    f"{row['平均变化量百分比']:.2f}%", 
                                    fontsize=9, ha='center', va='bottom', color=color
                                )
            
            # 设置右侧图表属性
            ax_decrease.set_title(f"{chinese_name} 净空单显著减小", fontsize=14)
            ax_decrease.set_xlabel(x_label, fontsize=12)
            
            if chart_type == "概率图":
                ax_decrease.set_ylabel('上涨概率', fontsize=12)
                ax_decrease.axhline(y=0.5, color='r', linestyle='--', alpha=0.7)
                if not ax_decrease.lines:  # 如果没有线条，设置默认范围
                    ax_decrease.set_ylim(0, 1.0)
                else:
                    ax_decrease.set_ylim(0, 1.0)  # 固定概率范围
            elif chart_type == "平均变化量百分比图":
                ax_decrease.set_ylabel('平均变化量百分比(%)', fontsize=12)
                ax_decrease.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
            else:  # 两者都显示
                # 设置双Y轴标签
                ax_decrease.set_ylabel('上涨概率', fontsize=12, color='blue')
                ax_decrease.tick_params(axis='y', labelcolor='blue')
                
                # 创建第二个Y轴用于平均变化量百分比
                ax_decrease2 = ax_decrease.twinx()
                ax_decrease2.set_ylabel('平均变化量百分比(%)', fontsize=12, color='green')
                ax_decrease2.tick_params(axis='y', labelcolor='green')
                
                # 添加参考线
                ax_decrease.axhline(y=0.5, color='r', linestyle='--', alpha=0.7)
                ax_decrease2.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
            
            ax_decrease.grid(True, linestyle='--', alpha=0.6)
            if ax_decrease.lines:  # 只在有线条时添加图例
                ax_decrease.legend(loc='best', fontsize=10)
            
            # 添加总标题
            method_str = self.curve_method_combo.currentText()
            figure.suptitle(
                f"{chinese_name} 净空单显著性变化分析 ({method_str})",
                fontsize=16, y=0.98
            )
            
            # 调整布局
            figure.tight_layout()
            figure.subplots_adjust(top=0.92)  # 为总标题留出空间
            
            # 保存图表对象
            self.last_significance_figure = figure
            
            # 在状态栏显示提示
            self.status_label.setText(f"显著性曲线已准备好")
            
            # 保存曲线数据供导出
            self.current_curve_data = {
                'increase': results['increase'],
                'decrease': results['decrease'],
                'contract': contract_type,
                'method': method,
                'x_label': x_label
            }
            
        except Exception as e:
            self.status_label.setText("绘制曲线出错！")
            err_msg = f"绘制显著性变化曲线时出错:\n{str(e)}\n\n{traceback.format_exc()}"
            self.stats_text.append(err_msg)
            QMessageBox.critical(self, "错误", err_msg)

    def export_curve_data(self):
        """导出曲线数据到CSV"""
        try:
            if not hasattr(self, 'current_curve_data'):
                QMessageBox.warning(self, "无数据", "没有可导出的曲线数据")
                return
                
            if not self.output_dir:
                QMessageBox.warning(self, "警告", "请先选择输出目录！")
                return
                
            # 获取当前参数
            contract_type = self.curve_contract_combo.currentData()
            chinese_name = self.get_chinese_name(contract_type)
            method = self.curve_method_combo.currentText()
            
            # 生成文件名
            filename = f"显著性曲线_{chinese_name}_{method}.csv"
            output_path = os.path.join(self.output_dir, filename)
            
            # 创建数据框
            all_data = []
            for change_type in ['increase', 'decrease']:
                for lag, data_list in self.current_curve_data[change_type].items():
                    for data in data_list:
                        data_row = {
                            '变化类型': '显著增大' if change_type == 'increase' else '显著减小',
                            '滞后天数': lag,
                            '阈值': data['阈值'],
                            '概率': data['概率'],
                            '平均变化量百分比': data.get('平均变化量百分比', np.nan),
                            '样本数': data['满足条件样本数'],
                            '总样本数': data['总样本数']
                        }
                        all_data.append(data_row)
            
            df = pd.DataFrame(all_data)
            df.to_csv(output_path, index=False, float_format='%.4f')
            
            QMessageBox.information(self, "导出成功", f"曲线数据已导出到:\n{output_path}")
            
        except Exception as e:
            QMessageBox.critical(self, "导出失败", f"导出曲线数据时出错:\n{str(e)}")
    
    def show_comparison_chart(self):
        """在新窗口中显示比较统计图表"""
        if not self.last_comparison_figure:
            QMessageBox.warning(self, "无数据", "请先执行比较统计分析")
            return
            
        # 创建或激活图表窗口
        if not self.chart_window:
            self.chart_window = self.ChartWindow(self)
        else:
            self.chart_window.figure.clear()
            
        # 将图表复制到新窗口
        self.chart_window.figure = self.last_comparison_figure
        self.chart_window.canvas.figure = self.last_comparison_figure
        self.chart_window.canvas.draw()
        
        # 显示窗口
        self.chart_window.show()
        self.chart_window.raise_()  # 置于顶层

    def show_significance_curve(self):
        """在新窗口中显示显著性变化曲线"""
        if not self.last_significance_figure:
            QMessageBox.warning(self, "无数据", "请先绘制显著性变化曲线")
            return
            
        # 创建或激活图表窗口
        if not self.chart_window:
            self.chart_window = self.ChartWindow(self)
        else:
            self.chart_window.figure.clear()
            
        # 将图表复制到新窗口
        self.chart_window.figure = self.last_significance_figure
        self.chart_window.canvas.figure = self.last_significance_figure
        self.chart_window.canvas.draw()
        
        # 显示窗口
        self.chart_window.show()
        self.chart_window.raise_()  # 置于顶层

    def toggle_quantile_range(self):
        """切换分位数范围控件的可用状态"""
        method = self.curve_method_combo.currentData()
        is_quantile = method == "quantile"
        
        self.quantile_label.setEnabled(is_quantile)
        self.quantile_start_spin.setEnabled(is_quantile)
        self.quantile_end_spin.setEnabled(is_quantile)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = IncrementalDataProcessor()
    window.show()
    sys.exit(app.exec_())